有关多继承的问题

一、多继承即一个子类有多个父类,它继承了多个父类的特性。
但多继承中会存在一个问题:即当某类的部分或全部直接基类是从另一个共同基类派生而来时, 在这些直接基类中从上一级共同基类继承而来的成员就拥有相同的名称,在派生类的对象中,这些同名的数据成员在内存中同时拥有多个副本,同一个函数名会有多个映射。这就是所谓的菱形继承问题(下面会给出图形),即A是B和C的直接基类,而D又继承了B和C。解决这一问题有两种方法:一种是使用作用域分辨符来唯一标识并访问它们,另一种是将共同基类设置为虚基类,这时从不同的路径继承过来的同名数据成员在内存中就只有一个副本,同一个函数名也就只有一个映射。
这里写图片描述
上图即为菱形继承问题。
下面就说说用虚基类来解决这一问题:


class D
{
public:
    D(){}
    void print(){cout<<"D:print"<<endl;}
    int m_i;
};
class A:virtual public D
{
public:
    A(int i = 0){}
    void print(){cout<<"A::print"<<endl;}  
private:
    int m_j;
};
class B:virtual public D
{
public:
    B(int j = 0):m_k(j){}
    void print(){cout<<"B::print"<<endl;} 
private:
    int m_k;
};
class C:public A,public B
{
public:
    C(){}
};
void main()
{
    C c;
    c.A::print();
    c.B::print();
    c.m_i = 2;   //C类对象可以直接访问D类的数据成员

二、虚基类及其派生类构造函数
先看以下代码:

class G
{
public:
    G(){cout<<"G"<<endl;}
};

class A
{
public:
    A(){cout<<"A"<<endl;}
};
class B:virtual public G
{
public:
    B(int i = 0):m_j(i){cout<<"B"<<endl;}
private:
    int m_j;
};
class C
{
public:
    C(){cout<<"C"<<endl;}
};
class D
{
public:
    D(){cout<<"D"<<endl;}
};
class F
{
public:
    F(){cout<<"F"<<endl;}
};

class E:public A, public B,virtual public C,public D
{
public:
    E(){cout<<"E"<<endl;}
private:
    F f;
};
void main()
{
    E e;   
}

运行结果如下:
这里写图片描述
主函数里声明了一个E类的对象,则先看E类,而E类又有多多个基类,运行时编译器会按照类的继承顺序先检查这每个基类是否有有基类,如果有基类也是虚继承则会先执行这个基类的虚基类,然后在回到当前,执行完本类的虚继承后按照继承顺序执行其他的。集合这段代码,因为E类有多个基类,所以先按照继承的顺序检查A,B,C,D是否还有基类,A没有,而B是虚继承了G,所以先执行了G,然后接着检查C,D,发现没有继承,然后在执行本类的虚继承,即E类是虚继承C,于是执行C,下来按照从前到后的顺序执行即A,B,然后是D,最后回到类的数据成员F,最后执行构造函数E。所以运行结果是G C A B D F E。
如果将代码改成:

class E:public A, public B, public C,virtual public D
{
public:
    E(){cout<<"E"<<endl;}
private:
    F f;
};

那么运行结果将会变为: G D A B C F E
这里写图片描述

用书上的话总结一下构造一个类的对象的一般顺序:
大概来说应该是:先继承,然后是初始化本类的数据成员,最后执行本类的构造函数。
具体来说:1. 如果一个类有直接或间接的虚基类,则先执行虚基类的构造函数 2. 如果该类也有其他基类,则按照它们在继承声明列表中出现的次序,分别执行它们的构造函数,但构造过程中,不再执行它们的虚基类的构造函数。 3. 按照在类定义出现的顺序,对派生类中新增的成员对象进行初始化。对于类类型的 成员对象,如果出现在构造函数的初始化列表中则以其中指定的参数执行构造函数,如未出现,则执行默认构造函;对于基本数据类型的成员对象,如果出现在构造函数的初始化列表中,则以其中制定的值为其赋初值。 4. 执行构造函数的函数体。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值