C++构造函数和析构函数的调用顺序

以下是两个测试BaseClass
class BaseClassA
{
public:
    BaseClassA(int a)
    {
        m_nNumberA = a;
        cout << "调用基类A的构造函数!\n";
        OutputDebugString("调用基类A的构造函数!\n");
    }

    ~BaseClassA()
    {
        cout << "调用基类A的析构函数!\n";
        OutputDebugString("调用基类A的析构函数!\n");
    }

private:
    int m_nNumberA;
};

class BaseClassB
{
public:
    BaseClassB(int b)
    {
        m_nNumberB = b;
        cout << "调用基类B的构造函数!\n";
        OutputDebugString("调用基类B的构造函数!\n");
    }

    ~BaseClassB()
    {
        cout << "调用基类B的析构函数!\n";
        OutputDebugString("调用基类B的析构函数!\n");
    }

private:
    int m_nNumberB;
};


测试一:
1 首先优先调用基类的构造函数,而不是自己的构造函数;
2 一般基类的构造函数是采取先到先构造的原则,也就是说根据基类继承的顺序依次调用构造函数;
3 基类构造函数调用完毕后,最后调用子类自己的构造函数;
4 当析构函数调用顺序确定后,析构函数的调用也确定了,完全和构造函数的调用顺序相反;

class DeriveClassA: public BaseClassA, BaseClassB
{
public:
    DeriveClassA(int a, int b)
        : BaseClassA(a)
        , BaseClassB(20)
    {
        m_nTestNumber = b;

        cout << "调用派生类A的构造函数!\n";
        OutputDebugString("调用派生类A的构造函数!\n");
    }

    ~DeriveClassA()
    {
        cout << "调用派生类A的析构函数!\n";
        OutputDebugString("调用派生类A的析构函数!\n");
    }

private:
    int m_nTestNumber;
};

void ConstructorTestA()
{
    DeriveClassA a(100, 200);

    //常规继承详细调用顺序
    //////////////////////////////////////////////////////////////////////////
    //1 首先调用DeriveClassA第一个继承对象BaseClassA的构造函数;
    //2 其次调用DeriveClassA第二个继承对象BaseClassB的构造函数;
    //3 调用DeriveClassA自己的构造函数
    //4 按照构造函数调用的相反顺序依次调用DeriveClassA的析构函数->BaseClassB的析构函数->BaseClassA的析构函数
}

调用基类A的构造函数!
调用基类B的构造函数!
调用派生类A的构造函数!
调用派生类A的析构函数!
调用基类B的析构函数!
调用基类A的析构函数!





测试二:
1 首先优先调用基类的构造函数,而不是自己的构造函数;
2 一般基类的构造函数是采取先到先构造的原则,也就是说根据基类继承的顺序依次调用构造函数;
3 基类构造函数调用完毕后,开始调用类变量(成员)的构造函数,构造函数和类的初始化列表相关,也就是说,这时候的成员初始化顺序决定了这部分类变量(成员)的构造函数调用次序;
4 基类构造函数和类变量(成员)的构造函数调用完毕后,最后调用子类自己的构造函数;
5 当析构函数调用顺序确定后,析构函数的调用也确定了,完全和构造函数的调用顺序相反;

class DeriveClassB: public BaseClassA, public BaseClassB
{
public:
    DeriveClassB(int a, int b)
        : BaseClassB(20) //这个两个故意调整顺序来确定基类构造函数和这个排版顺序无关
        , BaseClassA(a)  //
        , m_BaseClassB(a+b)
        , m_BaseClassA(200)
    {
        m_nTestNumber = b;

        cout << "调用派生类B的构造函数!\n";
        OutputDebugString("调用派生类B的构造函数!\n");
    }

    ~DeriveClassB()
    {
        cout << "调用派生类B的析构函数!\n";
        OutputDebugString("调用派生类B的析构函数!\n");
    }

private:
    int m_nTestNumber;
    BaseClassB m_BaseClassB;
    BaseClassA m_BaseClassA;
};

void ConstructorTestB()
{
    DeriveClassB b(100, 200);
    //带有对象成员的继承调用顺序
    //////////////////////////////////////////////////////////////////////////
    //1 首先调用DeriveClassA第一个继承对象BaseClassA的构造函数,这个和继承顺序有关,和构造函数初始化列表无关;
    //2 其次调用DeriveClassA第二个继承对象BaseClassB的构造函数;
    //3 调用DeriveClassA中成员对象m_BaseClassB的构造函数,这个顺序和构造函数初始化成员列表有关;
    //4 调用DeriveClassA中成员对象m_BaseClassA的构造函数;
    //5 调用DeriveClassA自己的构造函数
    //6 按照构造函数调用的相反顺序依次调用DeriveClassA的析构函数->BaseClassA的析构函数->BaseClassB的析构函数->BaseClassB的析构函数->BaseClassA的析构函数(这个应该是堆栈有关)

}

调用基类A的构造函数!
调用基类B的构造函数!
调用基类B的构造函数!
调用基类A的构造函数!
调用派生类B的构造函数!
调用派生类B的析构函数!
调用基类A的析构函数!
调用基类B的析构函数!
调用基类B的析构函数!
调用基类A的析构函数!






测试三:(首先这个测试是在测试一的基础上修改而来,和测试一对比可以了解虚继承类的构造调用)
1 首先优先调用基类的构造函数,而不是自己的构造函数;
2 如果有虚继承,应该首先确定虚基类的列表,依次优先调用虚基类的构造函数;最后再把一般的继承函数按照顺序依次调用构造函数;
3 基类构造函数调用完毕后,最后调用子类自己的构造函数;
4 当析构函数调用顺序确定后,析构函数的调用也确定了,完全和构造函数的调用顺序相反
class DeriveClassC: public BaseClassA, virtual public BaseClassB
{
public:
    DeriveClassC(int a, int b)
        : BaseClassA(a)
        , BaseClassB(20)
    {
        m_nTestNumber = b;

        cout << "调用派生类C的构造函数!\n";
        OutputDebugString("调用派生类C的构造函数!\n");
    }

    ~DeriveClassC()
    {
        cout << "调用派生类C的析构函数!\n";
        OutputDebugString("调用派生类C的析构函数!\n");
    }

private:
    int m_nTestNumber;
};

void ConstructorTestC()
{
    DeriveClassC a(100, 200);

    //虚继承详细调用顺序
    //重点:在多重继承中,优先考虑虚继承的类,对它优先初始化
    //////////////////////////////////////////////////////////////////////////
    //1 首先调用DeriveClassA第一个继承对象BaseClassB的构造函数;虽然BaseClassA继承排在BaseClassB的前面,但是BaseClassB是虚继承,所以优先构造;
    //2 其次调用DeriveClassA第二个继承对象BaseClassA的构造函数;
    //3 调用DeriveClassA自己的构造函数
    //4 按照构造函数调用的相反顺序依次调用DeriveClassA的析构函数->BaseClassA的析构函数->BaseClassB的析构函数
}

调用基类B的构造函数!
调用基类A的构造函数!
调用派生类C的构造函数!
调用派生类C的析构函数!
调用基类A的析构函数!
调用基类B的析构函数!




void ConstructorTest()
{
    ConstructorTestA();
    ConstructorTestB();
    ConstructorTestC();
}

猜你喜欢

转载自jacky-dai.iteye.com/blog/2305927