c++类多重继承初始化顺序

来源:互联网 发布:淘宝现在什么好卖2017 编辑:程序博客网 时间:2024/06/11 21:01

c++类多重继承时,初始化顺序是一个基础的问题,笔者每次清楚了以后,过段时间有点含糊了,有些基础的问题,在实际开发中,用到的频率少,今天做一个总结跟大家一起分享。

这里先讨论一般的基类继承时的初始化行为,然后再讨论虚基类的情况。

例子1:

#include<iostream>
using namespace std;

class CBase
{
    private:
         int a;
    public:
         CBase(int va):a(va)
        {
            cout<<"CBase:"<<endl<<this->a<<endl;
        }
};

class A:public CBase
{
    private:
        int t;
    public:
        A(int vt,int ut):t(vt),CBase(ut)
        {
            cout<<"A:"<<endl<<this->t<<endl;    
        }
    
};

class B:public CBase
{
    private:
        int t;
    public:
        B(int vt,int ut):t(vt),CBase(ut)
        {
            cout<<"B:"<<endl<<this->t<<endl;
        }
};

class C:public B,public A
{
    private:
        int t;
    public:
        //C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),CBase(kt)
        C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),t(kt)
        {
            cout<<"C:"<<endl<<this->t<<endl;
        }
};

int main()
{
    C cc(1,2,3);
    return 0;
}

输出结果:



注意类C的继承顺序和构造函数初始化顺序,蓝色和红色字体,刚好是相反的,从输出结果可以看到,是一继承顺序为依据的,先调用B的构造函数,B的构造函数执行前又先调用CBase基类的构造函数,然后是类成员的初始化,最后是类构造函数体部分的执行,以此类推,总结下顺序:

1、根据继承顺序,父类构造函数,回退到基类先开始

2、类成员初始化

3、构造函数体部分初始化

另外有一点需要注意的是,C类(红色字体部分)如果用注释掉的语句来初始化,会报错的,这里就引入了虚基类的问题,类似这种CBase类作为A和B类的共同基类,而C类又继承A和B,C++类在继承的时候,存在一个膨胀的问题的,所以才会有一种设计模式“尽量用聚合或者组合的方式”实现,而不是首先考虑继承,继承的层级过深,在性能和资源的占用上是需要衡量的,虚基类正好是可以解决这个问题的,今天重点讨论的是,虚基类继承的初始化行为,看下面的例子2:

#include<iostream>
using namespace std;

class CBase
{
    private:
         int a;
    
    public:
         CBase(int va):a(va)
        {
            cout<<"CBase:"<<endl<<this->a<<endl;
        }
        
};

class A:virtual public CBase
{
    private:
        int t;
    public:
        A(int vt,int ut):t(vt),CBase(ut)
        {
            cout<<"A:"<<endl<<this->t<<endl;    
        }
    
};

class B:virtual public CBase
{
    private:
        int t;
    public:
        B(int vt,int ut):t(vt),CBase(ut)
        {
            cout<<"B:"<<endl<<this->t<<endl;
        }
};

class C:public B,public A
{
    private:
        int t;
    public:
        C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),CBase(kt),t(kt)
        //C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),t(kt)
        {
            cout<<"C:"<<endl<<this->t<<endl;
        }
};

int main()
{
    C cc(1,2,3);
    return 0;    
}

输出结果:


对比例子1的结果,是有比较大的区别的,虚基类的构造要最先执行并且执行一次,当再次调用A类和B类的构造函数时,CBase类的构造函数不再调用执行,这也正说明了,虚基类解决了这种冗余继承时候的“膨胀”问题。

除了这个顺序不同外,其余的顺序还是一样的。