本文主要說明對象創建時構造函數的執行順序,對象成員的初始化順序;對象銷燬時析構函數的執行順序,對象成員的銷燬順序。
“對象的構造從類層次的最根處開始,在每一層中,首先調用基類的構造函數,然後調用成員對象的構造函數。析構則嚴格按照與構造相反的次序執行,該次序是唯一的,否則編譯器將無法自動執行析構過程。
一個有趣的現象是,成員對象初始化的次序完全不受它們在初始化表中次序的影響, 只由成員對象在類中聲明的次序決定。這是因爲類的聲明是唯一的,而類的構造函數可以有多個,因此會有多個不同次序的初始化表。如果成員對象按照初始化表的次序進行構造,這將導致析構函數無法得到唯一的逆序。”(引用自References[1])
從這裏看,每種語言特性的存在必有其原因,學習這些特性就是理解這些特性存在的原因。
下面的一段代碼是對上面這段話的說明,其中有4個類Foo, Bar, Base, Derived,它們的構造函數、拷貝構造函數、析構函數都有信息輸出。
- #include <iostream>
- using namespace std;
- class Foo
- {
- public:
- Foo() { cout << "Foo constructor" << endl; }
- Foo(const Foo &foo) { cout << "Foo copy constructor" << endl; }
- ~Foo() { cout << "Foo deconstructor" << endl; }
- };
- class Bar
- {
- public:
- Bar() { cout << "Bar constructor" << endl; }
- Bar(const Bar &bar) { cout << "Bar copy constructor" << endl; }
- ~Bar() { cout << "Bar deconstructor" << endl; }
- };
- class Base
- {
- public:
- Base() { cout << "Base constructor" << endl; }
- ~Base() { cout << "Base deconstructor" << endl; }
- };
- class Derived : public Base
- {
- public:
- Derived() { cout << "Derived constructor without arguments" << endl; }
- Derived(const Foo &foo, const Bar &bar);
- Derived(const Bar &bar, const Foo &foo);
- ~Derived() { cout << "Derived deconstructor" << endl; }
- private:
- Foo m_foo;
- Bar m_bar;
- };
- Derived::Derived(const Foo &foo, const Bar &bar) :
- m_foo(foo),
- m_bar(bar)
- {
- cout << "Derived constructor with argument[Foo foo, Bar bar] passed by references" << endl;
- }
- Derived::Derived(const Bar &bar, const Foo &foo) :
- m_bar(bar),
- m_foo(foo)
- {
- cout << "Derived constructor with argument[Bar bar, Foo foo] passed by references" << endl;
- }
- int main (int argc, char** argv)
- {
- Foo foo;
- Bar bar;
- cout << "test case 1:" << endl;
- Derived deri_1; // (1)
- cout << "test case 2:" << endl;
- Derived deri_2(foo, bar); // (2)
- cout << "test case 3:" << endl;
- Derived deri_3(bar, foo); // (3)
- cout << "test case end" << endl;
- return 0;
- }
執行結果是:
打印出的信息可分爲幾部分:
(1) 創建對象foo和bar ,執行Foo,Bar的構造函數
(2) Test Case 1:創建對象deri_1,首先執行基類Base的構造函數,其次執行成員m_foo,m_bar的構造函數來構造成員,最後調用自身的構造函數(無參數)。
(3) Test Case 2:創建對象deri_2,調用順序與case 1順序相同。
(4) Test Case 3:創建對象 deri_3,調用順序與case 1順序相同。注意到deri_2,deri_3的創建執行的是不同的Derived構造函數,雖然構造函數參數的順序不同,但是構造成員的順序是相同的。
(5) 銷燬對象deri_3,deri_2,deri_1,析構函數執行順序相同,與構造對象的順序相反。C++標準規定以對象聲明相反的順序銷燬這些對象。
(6) 銷燬對象bar,foo。
編譯運行環境:
- $ uname -a
- Linux localhost.localdomain 2.6.18-308.el5 #1 SMP Fri Jan 27 17:17:51 EST 2012 x86_64 x86_64 x86_64 GNU/Linux
- $ g++ --version
- g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-52)