OOP继承问题,虚基类, 建构子的构建顺序问题

这里我们说说多重继承(multiple inheritance)。 多重继承的问题涉及到建构子和析构子的调用顺序问题, 多重继承造成的歧义型(例如, 当多个base classes中有同名函数的时候, 如何解决,members from common base class如何解决(dimond的时候, 即类A继承自B, C, 但是B, C又同时继承自一个类D时候, 就出现歧义了, 此时A有两份关于D的拷贝))。 当涉及到vitual base class(虚基类)的时候, 初始化的顺序和调用顺序如何呢??

首先, 定义一个class具有多个parent class 很简单, 只需要list them one by one:

<span style="font-size:14px;">class Boo: public Bum, public Foo {// specify its own properties};</span>这样, 一个Boo类的object可以upcast(向上转型)为 Bum, 或者Foo, 因为Boo继承了其父类的一切, 虽然object无法看到父类的private的部分。

当我们创建一个Boo的object的时候, 需要先调用Bum 和 Foo的default constructor, 然后在调用自己的。 也就是说,先构造父亲的那一部分, 调用析构子的顺序和调用构造子的顺序相反。

如下:

#include <iostream>using namespace std;class Sofa {public:Sofa() {cout << "构造Sofa" << endl;}~Sofa() {cout << "析构Sofa" << endl;}void sit() {cout << "sit" << endl;}};class Bed {public:Bed() {cout << "构造Bed" << endl;}~Bed() {cout << "析构Bed" << endl;}void lie() {cout << "lie" << endl;}};class Sofabed: public Bed, public Sofa { //按照list的顺序构建,, 即继承的顺序public:Sofabed() {cout << "构造Sofabed" << endl;}~Sofabed() {cout << "析构Sofabed" << endl;}void sitAndLie() {cout << "sit and lie" << endl;}};int main() {Sofabed myfur;cout << endl;myfur.sit(); //继承的myfur.lie(); //继承的myfur.sitAndLie(); // 自己的return 0;}运行结构如下:

可见, 先构造父类的, 然后才是自己的。 当有多个父亲的时候, 则按照继承的顺序构造出来。

但是, 多重继承有很多的缺点, 不建议使用。

首先, 如果有两个parent classes有same name的members的时候, 就不好了。 此时当要使用这些同名的函数的时候, 我们只能用resolution operator解决冲突。 很不好。

另外, 只使用单继承更加的easier, less prone to error, 所以最好不要使用多重继承啦。

因为多重继承可以改写为单继承。 例如Sofabed可以单继承Sofa, 而Sofa里面有一个Bed的class。

不建议使用多继承。Anyway, 我们在多说一下多重继承。

派生类(derived class)必须为每一个继承的base class的建构子提供初始化的参数。 例如如下:

<span style="font-size:14px;">Cderived::Cderived(arg_B1, arg_B2, …, argBn, arg_Cderived):B1(arg_B1), B2(arg_B2), …, Bn(arg_Bn) {// initialize Cderived's own data members}</span>注意, 基类的建构顺序是以派生类继承的顺序调用的,而不是这里的初始化列表的顺序。当派生类调用自己的defalut copy constructor的时候, 编译器(compiler)会自动的调用基类的default copy constructor。

当复制构造函数是你自己定义的时候, 你就必须把相对应的参数传递给基类的copy constructor。

<span style="font-size:14px;">Cderived::Cderived(Cdedrived &c1): B1(c1), B2(c1), …, B(c1) {// copy the rest data members}</span>多重继承造成的歧义性有如下 两种情况:

(1)如果A继承了B, 和C, 但是B, C类中定义了同一个名字的函数func(), 此时A有两个同名的函数。 解决歧义的办法是使用B::func() 和 C::func()标识, 即resolution operator。 另外, 如果我们什么基类例如B型的一个pointer ptr的时候,然后将ptr指向派生类的时候, 用ptr指向派生类A的某个成员(假如这个成员在B中也有), 此时ptr指到的不是派生类A的这个成员, 而是基类的。 因为ptr会根据自己的type(为B)去调用相关函数(尽管ptr指向的地址是A的对象)。 此时的解决办法就是把这个同时出现在A和B的成员函数声明为virtual的(虚函数)。

(2) A继承了B,和C, 但是B, C 继承了同一个D, 此时A就继承了两份D的拷贝, 形成diamond的shape。 一份来自于B, 一份拷贝来自于C. 不仅造成在调用common base class的成员函数产生的歧义性, 而且浪费了空间。 解决的办法就是使用virtual base class(虚基类)。 即将D声明为虚基类(被其他类虚继承), 这样在做common base class的时候就只有一份拷贝了。

如下:

<span style="font-size:14px;">class CA { // common base classpublic:int x;CA(int a = 0) { x = a;}};clas CB: virtual public CA {public:int y;CB(int a, int b= 0): CA(a) { y = b}; // 注意CA(a)不能省略, 下同}class CC: virtual public CA {public:int z;CC(int a = 0, int b = 0): CA(a) { z = b;}};class CD: public CB, public CC {public:int w;CD(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0):CA(a), CB(a, b), CC(c, d) { // 注意只有CA(a)对x的设置有效, 后面的CB,CC无效w = e;}};</span>下面设置的时候:

<span style="font-size:14px;">CD obj(5, 4, 3, 2, 1);// x 为5, 后面的设置就无效</span>宣告为虚基类的时候, CA的构造函数值执行了一次。

多对自己说“我能行,我一定可以”,

OOP继承问题,虚基类, 建构子的构建顺序问题

相关文章:

你感兴趣的文章:

标签云: