1

我編寫了一個簡單的程序,以瞭解更多關於在C++中創建和銷燬對象的順序(使用Visual Studio 2015)。那就是:C++中的對象創建和銷燬順序

#include <iostream> 
#include <string> 

using namespace std; 

class A 
{ 
public: 
    A(string name) 
     : name(name) 
    { 
     cout << "A(" << name << ")::constructor()" << endl; 
    } 
    ~A() 
    { 
     cout << "A(" << name << ")::destructor()" << endl; 
    } 
private: 
    string name; 
}; 

class C 
{ 
public: 
    C(string name, A a) 
     : name(name), a(a) 
    { 
     cout << "C(" << name << ")::constructor()" << endl; 
    } 
    ~C() 
    { 
     cout << "C(" << name << ")::destructor()" << endl; 
    } 
private: 
    string name; 
    A a; 
}; 

class B 
{ 
public: 
    B(string name) 
     : name(name) 
    { 
     cout << "B(" << name << ")::constructor()" << endl; 
    } 
    ~B() 
    { 
     cout << "B(" << name << ")::destructor()" << endl; 
    } 
private: 
    string name; 
    A a1{"a1"}; 
    A a2{"a2"}; 
    C c1{"c1", a1}; 
    A a3{"a3"}; 
}; 

int main() 
{ 
    B b("b1"); 
    return 0; 
} 

輸出驚訝我一點點(在a1 S):

A(a1)::constructor() 
A(a2)::constructor() 
C(c1)::constructor() 
A(a1)::destructor() 
A(a3)::constructor() 
B(b1)::constructor() 
B(b1)::destructor() 
A(a3)::destructor() 
C(c1)::destructor() 
A(a1)::destructor() 
A(a2)::destructor() 
A(a1)::destructor() 

要詳細瞭解了事情的原委我增加了有關對象的實例的信息:

A(string name) 
     : name(name) 
    { 
     cout << "A(" << name << ")::constructor(), this = " << this << endl; 
    } 
    ~A() 
    { 
     cout << "A(" << name << ")::destructor(), this = " << this << endl; 
    } 

結果更令人驚訝:

A(a1)::constructor(), this = 0039FB28 
A(a2)::constructor(), this = 0039FB44 
C(c1)::constructor() 
A(a1)::destructor(), this = 0039F8A8 
A(a3)::constructor(), this = 0039FB98 
B(b1)::constructor() 
B(b1)::destructor() 
A(a3)::destructor(), this = 0039FB98 
C(c1)::destructor() 
A(a1)::destructor(), this = 0039FB7C 
A(a2)::destructor(), this = 0039FB44 
A(a1)::destructor(), this = 0039FB28 

也就是說,爲什麼a1的構造函數只叫一次和析構函數3次?我通過值a所以顯然至少創建了1個臨時對象,但請在和時向我解釋多少個A實例被創建和銷燬?

+5

你沒有註釋過很多其他類的構造函數,所以你只能看到部分圖片。 –

+0

@KerrekSB你是指打印出B和C類的'this'值嗎?或者是什麼? – NPS

+0

編譯器正在添加一些你看不到的默認構造函數。 – Galik

回答

5

正如評論中已經指出的那樣,A類型的對象也是通過copy-construction構造的,當您將它們作爲參數傳遞給值時。爲了看到這一點,你可以自己添加一個拷貝構造函數:

A(const A& other) 
: name(other.name) 
{ 
    cout << "A(" << name << ")::copy-constructor(), this = " << this << endl; 
} 

輸出示例:

A(a1)::constructor(), this = 0xbff3512c 
A(a2)::constructor(), this = 0xbff35130 
A(a1)::copy-constructor(), this = 0xbff350e8 
A(a1)::copy-constructor(), this = 0xbff35138 
C(c1)::constructor() 
A(a1)::destructor(), this = 0xbff350e8 
A(a3)::constructor(), this = 0xbff3513c 
B(b1)::constructor() 
B(b1)::destructor() 
A(a3)::destructor(), this = 0xbff3513c 
C(c1)::destructor() 
A(a1)::destructor(), this = 0xbff35138 
A(a2)::destructor(), this = 0xbff35130 
A(a1)::destructor(), this = 0xbff3512c 

Try it online

正如你可以看到,發生一個拷貝構造,當你將a1作爲參數傳遞給c1的構造函數,並在構造函數初始化其成員a時發生第二個。當c被銷燬時,臨時副本立即被破壞,而成員被破壞。

編輯:
Here您可以閱讀確切規則創建一個拷貝構造函數時。
爲了不創建默認的複製構造函數,僅提供任何用戶定義的構造函數是不夠的,它需要是複製/移動構造函數。

EDIT2:

從C++ 14標準(12.8複製和移動類對象)摘自:

7如果類定義不明確地聲明拷貝構造函數,一個被聲明含蓄。如果類定義聲明瞭移動構造函數或移動賦值操作符,則隱式聲明的拷貝構造函數被定義爲刪除;否則,它被定義爲默認(8.4)。如果類具有用戶聲明的複製賦值運算符或用戶聲明的析構函數,則不推薦使用後一種情況。