目录
Delegation(委托关系,Composition by reference)
类与类之间有些什么关系?
那么总结起来就三个:Composition(复合)、Delegation(委托)、Inheritance(继承)。
Composition(复合关系):has-a
下面先看一个模板里中抽出来的一段代码,
上面标黄的部分理解成一个和"deque<T>"一致的数据类型,为了阅读方便,直接将其进行替换,
这个意思表达的就是:在queue类中,有一个类型为“deque<T>”的变量c,这就是所谓的复合,口语化的讲就是,“我里面有另外一种东西”,详细点就是,在一个类中,含有其他类定义的对象在里面。表现出来就是一个类里面有一个(has-a)其他类定义的对象,当然也可以有多个。当代码量越来越多的时候,要习惯性的用图来表现,而不是用源代码,上图的右上角就是用图来表示这种关系。其中,queue类包含了deque类定义的对象,那么,queue类就加一个黑色的菱形符号来表示。
当然这个例子也是特殊的,就是queue类所有的操作都是建立在deque上的,这是比较独特的。
Composition(复合)关系下的构造与析构
这个过程是编译器自动完成的。
下面的示意图,由于是左边的拥有右边的所以,左边的叫"Container",右边的叫"Component".从内存的角度看,就是右侧的浅蓝色的部分:
而当构造的时候,外界要先调用内部的默认构造函数,再执行自己(构造要由内而外);反过来,析构要由外而内,先执行自己的,再去调用内部的析构函数(析构的时候没有默认的概念)。上图中的标红部分,都是编译器自动加的,帮编程的语言尽量合理化。
Delegation(委托关系,Composition by reference)
由于类中带有指针,这个指针指向另外的一个类,这种就是所谓的委托关系。
通过右上角的图可以看见,一个空的菱形来表示这种关系,可以看出,这种关系这种关系有点虚,不是"实"的。
如果这种关系是用指针相连的,那就存在它们之间的生命周期不一致的问题。但是,这种设计方式非常的经典,就是我当前字符串该有什么东西(充当对外的接口),我不在这个对应的类写出来,我用其他类写出来(真正的实现),然后以委托的方式跟当前类建立关系。这种方式甚至可以称之为"编译防火墙",左边永远不用再编译,只需要编译右边就可以了!
那么在看上图下方的示意图,描述就是,多个对象,是由一个委托的类构造的,那么这个委托的类的关键功能,再由其他的类来具体的实现。
这里也有一个问题,由于a,b,c都是共享的"Hello"的内容,当然它们彼此之间是不知道的,那么,我们一定要保证,当修改a中的"Hello"内容的时候,不要去影响到b或者c的内容。所以,要确保该其中一个的时候,系统要单独拿(copy 的方式)一份数据给它改,而不影响其他的。改完之后,a独享一份数据,b,c共享一份数据。
Inheritance(继承关系):is-a
这里的struct可以理解成就是class,通过继承来实现另一个子类,右侧是对应的示意图。
这里面是借用继承的观念,子类可以去使用父类的数据,就好像子辈可以继承父辈的遗产。当然,这并不是继承最有价值的地方,继承的最有价值的地方,是跟虚函数结合使用的时候。
Inheritance(继承)关系下的构造和析构
这个过程是编译器自动完成的。
继承就是子类的对象里面有父类的成分在里头,也是一种外部包内部的结构,所以,参考复合关系中构造和析构函数的调用顺序,就可以理解了!
这里面也有一个良好的编程习惯要求:基类中的析构函数必须是虚函数,否则会出现undefined behavior。只要你认为你所设定的当前类要作为父类或将来要被作为父类使用,那就需要把其析构函数设定为虚函数使用(具体原因,下篇博客详解)