預備博客:
C++虛繼承中構造函數和析構函數順序問題以及原理
C++派生類含有成員對象構造函數析構函數順序
C++虛基類成員可見性
程序一如下:
#include<iostream>
using namespace std;
class A {
public:A(int a) :x(a) { cout << "A constructor..." << x << endl; }int f() { return ++x; }~A() { cout << "destructor A..." << endl; }
private:int x;
};
class B :public virtual A {
private:int y;A Aobj;
public:B(int a, int b, int c) :A(a), y(c), Aobj(c) { cout << "B constructor..." << y << endl; }int f() {A::f();Aobj.f();return ++y;}void display() { cout << A::f() << "\t" << Aobj.f() << "\t" << f() << endl; }~B() { cout << "destructor B..." << endl; }
};
class C :public B {
public:C(int a, int b, int c) :B(a, b, c), A(0) { cout << "C constructor..." << endl; }
};
class D :public C, public virtual A {
public:D(int a, int b, int c) :C(a, b, c), A(c) { cout << "D constructor..." << endl; }~D() { cout << "destructor D...." << endl; }
};
int main()
{D d(7, 8, 9);d.f();d.display();return 0;
}
同時還要注意調用函數的時候順序為從右往左。
解析:首先我們調用D的構造函數,發現D虛繼承了A,直接繼承了C,間接繼承了B,B中含有成員對象Aobj,因此構造函數的調用順序為:
A(9)
【首先調用虛基類的構造函數,輸出A constructor...9
】
A(9)
【接下來調用B的構造函數,因為B含有成員對象Aobj,所以先調用Aobj的構造函數,輸出A constructor...9
】
B(7,8,9)
【運行B的構造函數,輸出B constructor...9
】
C(7,8,9)
【運行C的構造函數,輸出C constructor...
】
D(7,8,9)
【運行D的構造函數,輸出D constructor...
】
d.f()
【因為d中沒有f
方法,因此我們在其基類中找,發現其間接基類B和虛基類A中含有方法f
,但是B中的方法優先級更高,因此訪問的是B中的方法,B中的方法f
會調用A中的方法f
,A::x=10
,然后調用Aobj.f()
,則Aobj.x=10
,然后y=10
】
d.dispaly()
【運行B的方法,因為輸出的時候是從右往左輸出的,所以先調用B中的方法f
,此時A::x=11
,Aobj.x=11
,y=11
,同時函數返回11,然后再調用Aobj.f()
,返回12,再調用A::f()
,返回12,輸出12 12 11
】
~D()
【開始析構,調用順序和調用構造函數的順序相反,先是D,然后再調用C的,調用B的,調用Aobj的,調用A的,輸出destructor D....
】
~C()
【沒有輸出】
~B()
【輸出destructor B...
】
~A()
【輸出destructor A...
】
~A()
【輸出destructor A...
】
運行結果:
程序二如下:
#include <iostream>
using namespace std;
class Base1
{
public:Base1(){cout << "class Base1!" << endl;}
};
class Base2
{
public:Base2(){cout << "class Base2!" << endl;}
};
class Level1 :public Base2, virtual public Base1
{
public:Level1(){cout << "class Level1!" << endl;}
};
class Level2 : public Base2, virtual public Base1
{
public:Level2(){cout << "class Level2!" << endl;}
};
class TopLevel :public Level1, virtual public Level2
{
public:TopLevel(){cout << "class TopLevel!" << endl;}
};
int main()
{TopLevel obj;return 0;
}
解析:理解這個程序需要對含有虛基類的構造順序有比較深刻的認識。
類TopLevel
直接繼承了Level1
,虛繼承了類Level2
,然后這兩個類又直接繼承了類Base2
,虛繼承了類Base1
,因此最后類TopLevel
虛繼承了類Base1
和類Level2
。
由虛基類首先進行構造可知,我們首先運行的是類Base1
的構造函數
【輸出class Base1!
】
然后運行類Level2
的構造函數,發現虛基類Base1
已經構造,則構造直接繼承的類Base2
【輸出class Base2!
】
【輸出class Level2!
】
再依次運行非虛基類,即類Level1
的構造函數
【輸出class Base2!
】
【輸出class Level1!
】
最后運行TopLevel
的構造函數
【輸出class TopLevel!
】
運行結果: