簡體   English   中英

使用繼承時構造函數/析構函數調用的順序

[英]Order of Constructor/Destructor Calls When Using Inheritance

我試圖通過編寫一些示例代碼並嘗試遵循程序流程來了解構造函數和析構函數的調用順序。 在大多數情況下,我能夠理解(在Google需要的幫助下)。 但是,在一個特殊情況下,我遇到了一些障礙。

這是我正在使用的程序:

#include <iostream>
class baseC
{

public:
        baseC() { std::cout << "Calling constructor of base class: " << std::endl; }
        virtual char const * getName(){ return "Base Class";}
        ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;}
};

class childC : public baseC
{
public:
        childC() { std::cout << "Calling constructor of child class: " << std::endl; }
        char const * getName(){ return "Child Class";}
        ~childC(){ std::cout << "Calling destructor of child class: " << std::endl; }
};

int main()
{
        baseC c3 = childC();
        std::cout << c3.getName() << std::endl;
}

這是我得到的輸出:

$ g++ test_vd_se.cpp -o test; ./test
Calling constructor of base class: 
Calling constructor of child class: 
Calling destructor of child class: 
Calling destructor of base class: 
Base Class
Calling destructor of base class:

編譯器似乎首先創建一個基類和子類(這是預期的),但它繼續銷毀這兩個類,但它可以從基類調用一個成員函數,然后繼續再次銷毀基類。

如果有人能解釋為什么按順序調用函數,我將不勝感激。

這里的問題是你正在切割對象。

baseC c3 = childC();

將創建一個臨時childC ,然后將該對象復制到c3 這就是你看到的原因

Calling constructor of base class:  // create base part of temporary
Calling constructor of child class: // create temporary

// the copy happens here but you do not output when copying

Calling destructor of child class:  // destroy base part of temporary
Calling destructor of base class:   // destroy temporary

這樣做的正確方法是使用智能指針。 如果你將main()更改為

int main()
{
        auto c3 = std::make_unique<childC>();
        std::cout << c3->getName() << std::endl;
}   

或者,如果您無法訪問智能指針:

int main()
{
        baseC* c3 = new childC();
        std::cout << c3->getName() << std::endl;
        delete c3;
}

你得到:

Calling constructor of base class: 
Calling constructor of child class: 
Child Class
Calling destructor of child class: 
Calling destructor of base class: 

實例

我們還需要使~baseC() virtual以便調用正確的析構函數。

virtual ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;}

您還將注意到現在打印Child Class而不是Base Class因為現在我們有一個指針動態調度啟動並調用正確的虛函數。

“異常”來自以下任務:

baseC c3 = childC();

在哪里首先創建一個臨時的childCchildC按順序調用構造函數:

Calling constructor of base class: 
Calling constructor of child class:

然后進行賦值,因此創建了一個baseC對象。 但這一次,它不是您調用的構造函數,而是默認的復制構造函數 這就是為什么我們沒有再次觀察Calling constructor of base class:用於構造對象c3 )。 要證明這一點,請嘗試將復制構造函數添加到baseC類:

  baseC(const baseC& other) { std::cout << "Calling Copy-constructor of base class: " << std::endl; }

使用相同的main函數,您將在輸出中觀察兩次句子:

Calling constructor of base class: 
Calling constructor of child class:
**Calling copy-constructor of base class:**
Calling destructor of child class: 
Calling destructor of base class: 
Base Class
Calling destructor of base class:

最后,臨時子對象被銷毀,因此從下到上調用析構函數。

Calling destructor of child class: 
Calling destructor of base class: 

現在baseC對象c3仍然存在,調用了getName()方法,該方法輸出:

Child Class

然后當變量c3超出范圍( main()結尾)時, c3被銷毀:

Calling destructor of base class:

最后, baseC& c3 = ChildC();會有所不同baseC& c3 = ChildC(); (使用VS2015編譯,我不確定它是否符合C ++ 14標准),它不會創建兩個對象但只創建一個。 然后順序是:

contruction of baseC
contruction of childC
destruction of childC
destruction of baseC

最后,將析構函數聲明為虛擬總是更安全和更好的做法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM