C++ 组合与继承
组合与继承
Composition(复合),表示has-a
复合关系简单来说,就是一个类中有另外一个类,A类中需要实现的东西,完全可以由B类中的方法来实现的话,就不需要在A类中去写,而直接去调用B类中的方法就行了。
template <class T, class Sequence = deque<T>> class queue{ ... protected: sequence c; //底层容器 public: bool empty() const {return c.empty();} size_type size() const {return c.size();} reference front() {return c.front();} reference back() {return c.back();} void push(const value_type& x) {c.push_back(x);} void pop() {c.pop_front();} };
因为单向队列类queue中的一些操作完全可以由双端队列类deque中的方法去实现,所以可以在queue类中直接加入底层容器去调用deque中的方法,而不是自己实现,这就是复合。
内存关系:
template <class T>
class queue{
protected:
deque<T> c;
...
};
template <class T>
class deque{
protected:
Itr<T> start;
Itr<T> finish;
T** map;
unsigned int map_size;
}
template <class T>
struct Itr{
T* cur;
T* first;
T* last;
T** node;
....
};
算内存,由内而外,首先看结构体Itr,一个指针4字节,所以sizeof(Itr) = 4*4 =16
然后在 class deque中,有两个Itr结构体 加一个指针变量一个整形变量,所以sizeof(deque) = 16 * 2 + 4 + 4 = 40;
同理,所以sizeof(queue) = 40;
Composition(复合)关系下的构造和析构
构造由内而外
Container 的构造函数首先调用 Component 的 default 构造函数,然后才执行自己。
Container::Container(...): Component() {...}; //因为Component的构造函数可能不止一个,编译器不知道调用哪个,所以只会调用缺省构造函数,除非自己指定参数。
析构由外而内
Container 的析构函数首先执行自己,然后才调用Component的析构函数
Container::~Container(...){... ~Component()};
组装都是由内而外的,拆开肯定只能由外而内。
Delegation(委托).Composition by reference;
委托其实跟复合很像,说实在点也就是定义上有点区别。
不过委托就是Composition(复合) by reference,看这个应该就能明白了。
//file String.hpp class StringRep; class String{ public: String(); String(const char * s); String(const String& s); String &operator = (const String& s); ~String(); .... private: StringRep * rep; };
//file String.cpp #include"String.hpp" namespace { class StringRep{ friend class String; StringRep(const char * s); ~StringRep(); int count; char * rep; }; } String::String(){...} ...
通过调用 StringRep指针来获取到字符串"Hello",通过下图就很明显能够看出。
这种模式的优点就是无论你怎么修改StringRep 对客户所需要看到的Hello都不影响,甚至可以更换委托对象。维护和更新起来特别方便。
可以说这两者之间就是 Handle 与 Body 的关系。
Inheritance(继承) ,表示is-a
struct _List_node_base { _List_node_base* _M_next; _List_node_base* _M_prev; }; template<typename _Tp> struct _List_node :public _List_node_base { _Tp _M_data; };
语法上继承有3种方法,最常用的就是 :public ...
其实struct和class是非常相似的,所以这里拿struct举例,继承是is-a的关系
继承在面向对象中的概念来说,就是父类与子类,比如说人类分男人,女人,人类是父类,男人与女人是子类。
拥有父类-人类的属性的同时,多了自己的属性。
构造由内而外
Derived 的构造函数首先调用Base的 default 构造函数,然后才执行自己。
Deruved::Derived(...) : Base() {...};
析构由外而内
Derived 的析构函数首先执行自己,然后才调用Base的析构函数。
Derived::~Derived(...){...~Base()};
注意:base class 的 dtor必须是virtual,否则会出现 undefined behaviod
养成习惯,每当创建的类有可能会作为基类衍生子类时,最好把这个类的析构函数设为虚函数(virtual)