组合与继承
一、类与类之间的关系
1.Inheritance(继承),表示is-a
1.1从语法角度看
struct _List_node_base
{
_List_node_base* _M_next;
_List_node_base* _M_prev;
}
template<typename _Tp>
struct _List_node
:public _List_node_base//我要继承上面的那个类,这是一种public的继承
{
_Tp _M_data;
}
从子类向父类画一个空心三角形
1.2从内存的角度看
由派生类(子类)指向父类,子类的对象中有父类的成分
构造由内而外(这样才坚固)
子类的构造函数首先调用父类的默认构造函数,然后才执行自己。
Derived::Derived(...) : Base() {...};
析构由外而内(这样才稳定)
子类的析构函数首先执行自己,然后才调用父类的析构函数。
Derived::~Derived(...) {...~Base() };
父类的析构函数必须是virtual的,否则会出现undefined behaviour。
2.Composition(复合),表示has-a
2.1从语法角度看
一个类中含有其他类
queue是容器,拥有deque
public中写的函数全部由c的操作函数完成
deque已经很棒了,假设有100个功能
但是客户需要queue,功能减少很多,名称也不一样,那就改造一下deque好了
这称为一种Adapter
2.2从内存的角度来看
2.3复合关系下的构造函数和析构函数
一个容器由组成部分构成
构造由内而外
Container的构造函数首先调用Component的默认(default)构造函数,然后才执行自己。
若不想调用默认的,就需要自己在下列代码中写
Container::Container(...):/*构造函数的初值列*/ Component() {...};
先执行红色部分,其实component()是编译器帮我们加上的
析构由外而内
Container的析构函数先执行自己,然后才调用Component的析构函数
Container::~Container(...) {...~Component() };
这些都是编译器帮我们做的,设计者只需自己管自己编好自己的big three即可
3.Delegation(委托)Composition by reference
一个类的private中有一个指针指向另一个类
虽然左边有一个指向右边,但是有点虚,只是一个指针而已,具体什么时候真正拥有,目前还不知道。
我拥有一个指针指向你之后,在任何一个我想要的时间,我都可以调用你,把任务委托给你。
上边这张图是十分有名的一种写法,左边直接是用户可以看到的,对于string这个函数有哪些操作,然后通过指针连接到右边的类,具有很好的弹性,因为当程序员想要修改的时候,左边的代码不需要改动。
abc三个指针公用一个hello,当a想要改动hello时,系统给出一个副本让a改动,b和c继续共享同一个hello,这种操作叫copy on write