[英]C++ destructors question
關於下面的示例代碼,為什么基類的析構函數被調用兩次?
class Base {
public:
Base() {
std::cout << "Base::Base()" << std::endl;
}
~Base() {
std::cout << "Base::~Base()" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived::Derived()" << std::endl;
}
~Derived() {
std::cout << "Derived::~Derived()" << std::endl;
}
};
int main() {
Base a = Derived();
return EXIT_SUCCESS;
}
以下是程序運行時輸出的示例:
Base::Base()
Derived::Derived()
Derived::~Derived()
Base::~Base()
Base::~Base()
發生的事情稱為切片。 使用Derived
類型的對象初始化Base
類型的對象。 由於任何Derived
類型的對象都有一個Base
類型的對象(稱為“基類子對象”),因此在整個程序中將存在兩個Base
對象和一個Derived
對象。 Derived對象(及其Base
類型的Base
類子對象)僅在初始化時存在,而剩余的Base
對象存在直到main
結束。
由於有兩個Base對象和一個Derived對象,因此您還將看到另外一個Base destructors運行。
正在使用復制構造函數。 如果要查看發生了什么,請檢查復制構造函數:
Base( const Base & ) {
std::cout << "Base::Base( const Base &)" << std::endl;
}
和Derived類似。
請注意,這與析構函數不是虛擬的無關。
當你在main()
說Derived()
,它會創建一個臨時對象,然后將其復制到對象a中。 因此有兩個對象,因為析構函數被調用兩次。 另外,正如其他人指出你的基類析構函數應該是虛擬的。
因為你創建一個臨時類型的Derived
之前拷貝構造a
與它。 所以這基本上是發生了什么:
Derived d(); // Your temporary of type Derived is created
Base a(d); // The temporary is used to call a's copy constructor
d.Derived::~Derived(); // The temporary is destroyed, calling both ~Derived and ~Base
a.Base::~Base(); // The nonvirtual destructor on a is called, so ~Base is called, but not ~Derived
因此除了開頭不必要的復制(編譯器可能優化掉)之外,實際的錯誤是~Base不是虛擬的。
編輯哎呀,完全錯過了litb指出的切片。 請改為閱讀他的回答:)
添加以下內容將使程序更加清晰:
Base(const Base& base){
std::cout << "Base::Base(const Base& base)" << std::endl;
}
編譯器將自動為您創建一個復制構造函數。 通過自己定義,(並通過添加打印),您可以看到構造函數和析構函數的數量匹配
Base::Base()
Derived::Derived()
Base::Base(const Base& base)
Derived::~Derived()
Base::~Base()
Base::~Base()
你需要一個虛擬的析構函數 。
你有一個堆棧變量和一個臨時變量 - 總共構造了兩個對象 - 因此析構函數被調用兩次是合乎邏輯的。
1)構建Derived類型的臨時對象(調用Derived :: Derived()和Base :: Base())
2)臨時對象被復制到“a”
3)銷毀臨時對象(Derived :: ~Derived()和Base :: ~Base()被調用)
4)返回EXIT_SUCCESS;
5)“a”被銷毀,因此調用Base :: ~Base()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.