Inheritance 继承
Composition 组合/复合
Delegation 委托
`Object Oriented Programming: OOP`
`Object Oriented Design: OOD`
1 Inheritance
1. 内存角度

2. Inheritance 下的 构造 和 析构:
与Composition 思路相同
(1) 构造 由内而外
Derived::Derived(...) : Base() { ... }
(2) 析构 由外而内
Derived::~Derived(...) : { ... ~Base() }
`Base 的 dtor 必须是 virtual, 否则 可能出现 undefined behavior`
=> class 现在或将来 可能是 父类, 则将其 dtor 设为 virtual
3. 带虚函数 的 继承: 继承 + 虚函数 才有威力
(1) non-vf: 不希望 子类 override (redefine) 自己
`本类 己经实现, 且 不让子类再做动作`
(2) vf: 希望 子类 override ( redefine ) 自己, 且 自己有 默认定义
=> 非纯虚
`本类 可以实现, 且 允许子类 有自己的新实现`
(3) pvf: 希望 子类 override 自己, 且 自己 没 默认定义
=> 纯虚
`本类 没法实现, 但 想提供统一接口, 让子类去实现`
class Shape
{
public:
int generateId();
virtual void error(const std::String& msg);
virtual void draw() const = 0;
};
4. 数据/函数 继承
数据 继承: 内存角度
函数继承: 继承的是 函数的 调用权
, 子类 可以调用 父类的函数
2 Composition
1. deque
template <class T, class Sequence = deque<T> >
class queue
{
...
protected:
Sequence c; //底层容器
public:
// 以下 完全用 c 的 函数完成
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() const { return c.front(); }
reference back() const { return c.back(); }
// deque 是两端可进出,queue 是末端进前端出
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
母语: /`dek/
黑色菱形: 其中有东西
类 queue 拥有 1个 东西 c, c 的类型 是 deque<T>
Composition: 类 A 拥有(1/n 个) 类B, 2个类的关系为 has-a
A (queue) 借用 B( deque ) 已有功能 来实现 自己的功能
— 设计模式 Adapter
内存角度 看 Composition


2. Composition 下的 构造 和 析构
(1) 内存角度

(2) 构造 由内而外:
这样 基础才稳定
Container ctor 先调 Component ctor,
然后才 执行自己
若 Container ctor 函数名 和 函数体 之间
`不 explicitly 调 Component` 的 ctor,
则 编译器 隐含自动加 default ctor
Container::Container(...): Component() { ... }
(3) 析构 由外而内:
像一层层 剥洋葱一样
Container 的 dtor 先执行自己, 然后 调用Component 的 dtor
若 Container dtor 最后 `不 expliciy 调Component` 的 dtor
则 编译器 隐含自动调 default dtor
Container::~Container(...): { ... ~Component() }
3 Delegation: Composition by reference
左边 拥有一个 右边的指针, 即 左边 用 指针 指向 右边
=> 这个 拥有 不是 立即拥有,
而是 在需要右边的时候 才 通过右边的指针 调用右边,
把任务 委托 给 右边
=> 左 右 不同步: 左右生存期不同, 左 需要 右时, 才创建右
委托 没有 Composition by reference 表达得清晰
`学术界 不讲 by pointer, 只讲 by reference,
即使是用 pointer 在传`
// file String.hpp
class StringRep;
class String
{
public:
String();
String(const char* s);
String(const string& s);
String& operator = (const String& s);
private:
StringRep* rep; //---pimp
};
//file String.cpp
#include "String.hpp"
namespace
{
class StringRep
{
StringRep(const char* s);
~StringRep();
int count;
char* rep;
}
}
String::String()
{ ... }
编译防火墙:
左边 对外 interface 不变, 右边 怎么变动都行, 都不影响 client

4 Inheritance with virtual -> 多态
1. Template Method
本类/framework 先实现 自己能实现的 func, 自己 不能实现的 func 设为 vf, 留给 子类/application 去实现
windows 下 通过菜单 逐级寻找, 最后 打开文件 的 framework:
framework 中 无法完成 打开文件, 交给 具体 application
`Template Method` 的 一个 `example:`
`函数 OnFileOpen 的 implement 方法`,
该 example 也是 `MFC(Microsoft Foundation Classes)`
这套卖钱的产品 中 `大量使用 的 手法`



myDoc.OnFileOpen() 引发 静+动 态绑定
(1) 用 Derived obj 调 Base non-vf / vf => &obj 作 arg 传给 implicit para this => 均 静态绑定
myDoc.OnFileOpen(): &myDoc 作 arg 传给 CDocument::OnFileOpen() 的 implicit para this ( CDocument* )
=>
形实结合 CDocument* this = &myDoc
即
&myDoc/CMyDoc* upcast 为 CDocument*
(2) Base non-vf 又用 多态 ptr 调 vf => 动 态绑定
CDocument::OnFileOpen()
又 用 多态 ptr this (this: Base ptr 指向 derived_obj myDoc)
调 vf / this->Serialize()

#include<iostream>
//------ FrameWork
class CDocument
{
public:
void OnFileOpen();
//这里 Serialize() 为 纯虚函数 或 空函数 都可以
virtual void Serialize() = 0;
//virtual void Serialize(){ }
};
void CDocument::OnFileOpen()
{
//如下 每个 std::cout 表示一个 实际动作
std::cout << "dialog..." << std::endl;
std::cout << "check file status..." << std::endl;
std::cout << "Open file..." << std::endl;
//(3) this->Serialize()
// 父类 non-virtual func 中 用 pointer this 调
// 父类 pure virtual func =>
// this -> 运行时 obj/myDoc 的 vptr -> vtbl
// -> 子类中 override 的 vf
Serialize();
std::cout << "close file..." << std::endl;
std::cout << "update all views..." << std::endl;
}
//------ App
class CMyDoc : public CDocument
{
public:
virtual void Serialize()
{
//(4) 子类 override 父类 pure vf
// reason: 只有 application 本身 才知道 如何读取自己的文件
std::cout << "CMyDoc::Serialize()..." << std::endl;
}
};
int main()
{
//(1) create subclass obj
CMyDoc myDoc;
//(2) 通过 子类 obj 调 继承自 父类 的 non-virtual func
// =>
// 1) 静态绑定
// 2) &obj_subclas 作 arg 传给 父类 该 func 的 para this:
// CDocument::OnFileOpen(&myDoc)
// => 形实结合 CDocument* this = &myDoc
// => this/CDocument* downcast 为 &myDoc/CMyDoc*
myDoc.OnFileOpen();
}



myDoc.OnFileOpen():
通过 obj 调 非虚函数 => 静态绑定

this->Serialize():
通过 父类 pointer 调 子类对象 的 虚函数 => 动态绑定

2. 动/静 态绑定 本质区别
(1) `静态绑定 ( 通过 obj 调用 vf/non-vf ):`
编译时, 把 相应 statement 编译成 func addr
, 运行/调用时 直接 call 固定的 func addr

(2) `动态绑定 ( 必须 通过 ptr/ ref 调 vf ):`
运行时, 才能 通过 &obj -> vptr -> vtbl -> call 的 vf ptr

5 Inheritance + Composition 下的 构造 和 析构

#include<iostream>
using namespace std;
class Base
{
public:
Base() { cout << "Base..." << endl; }
};
class Component
{
public:
Component() { cout << "Component..." << endl; }
};
class Derived : public Base
{
public:
Derived() { cout << "Derived..." << endl; }
private:
Component component;
};
int main()
{
Derived derived;
}
//---print
Base...
Component...
Derived...
6 Inheritance + Delegation: 功能强大
Observer 模式
UI ( User Interface )
程序设计
文件:1份存储, 多种表现
多种表现:
1) powerPoint: 4个窗口 看同一格式的 该文件 — 相同 Observer
2) Graph: 不同 角度 ( 曲线 / 散点图) 看 该文件 — 不同 Observer
左边 ( 放 data ) 拥有 多个 右边的 pointer ( 观察 左边 data ) => 把 View 功能 delegate 给 右边 各具体 Observer
client 可以开 多个 Observer
左 `pimp` 右

网友评论