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(); }