[英]Why is there a memory leak when an exception is thrown from a constructor?
我讀過Paul Deitel撰寫的C ++ How to Program 8th Edition 。 第645頁有一個聲明:
當從構造函數中拋出在新表達式中創建的對象的異常時,將釋放該對象的動態分配內存。
為了驗證這個語句,我編寫了如下代碼:
#include <iostream>
#include <exception>
#include <memory>
class A{
public:
A(){std::cout << "A is coming." << std::endl;}
~A(){std::cout << "A is leaving." << std::endl;}
};
class B
{
public:
B()
{
std::cout << "B is coming." << std::endl;
A b;
throw 3;
}
~B(){std::cout << "B is leaving." << std::endl;}
};
int main(void)
{
try
{
std::shared_ptr<B> pi(new B);
}
catch(...)
{
std::cout << "Exception handled!" << std::endl;
}
}
輸出是:
B is coming.
A is coming.
A is leaving.
Exception handled!
這表明B的析構函數沒有被調用,這似乎與上面的陳述相沖突。
我的代碼是否正確以驗證聲明? 如果沒有,我應該如何修改它? 如果是,這是否意味着該陳述是錯誤的?
你混淆了兩件事:
你已經證明后者沒有發生,這是有道理的:你怎么能破壞那些沒有正確構造的東西? 請注意,成員變量將調用其析構函數,因為在構造函數拋出異常時,所有成員變量都已完全構造。
但是,沒有任何內存被釋放,這無疑將會發生。
[C++11: 15.2/2]:
任何存儲持續時間的對象,其初始化或銷毀由異常終止,將為其所有完全構造的子對象執行析構函數(不包括類似聯合的類的變體成員),也就是說,對於主要構造函數(12.6.2)已完成執行且析構函數尚未開始執行的子對象。 類似地,如果對象的非委托構造函數已完成執行,並且該對象的委托構造函數以異常退出,則將調用該對象的析構函數。 如果對象是在new-expression中分配的,則調用匹配的釋放函數(3.7.4.2,5.3.4,12.5)(如果有)以釋放對象占用的存儲空間。
這意味着B
的ctor中的所有內容都是異常點,即破壞。 B
本身的實例從未構建過,因此不能破壞它。 另請注意, pi
從未構建過。
std::shared_ptr<B> pi(new B) - start with new B
new B - triggers the ctor of B
std::cout ... - the output
A b; - construct an A
throw 3; - calls ~A()
- rewind, new B is "aborted"
- std::shared_ptr<B> pi(new B) is "aborted"
你可以修改你的代碼,看看std::shared_ptr
的構造函數永遠不會被你的新類替換掉,取一個指針:
struct T {
T(B*) { std::cout << "T::T()\n"; }
};
...
try
{
T pi(new B); // instead of std::shared_ptr<B> pi(new B);
}
...
T
的構造函數不會被命中(參見“ pi
從未被構造”)。
現在假設B
的構造函數將分配內存,如下所示:
B()
{
A* a = new A(); // in contrast to A a;
throw 3;
}
以前A::~A()
被調用,這是一個被解構的,我們現在有一個指針,指針不需要解構。 但是, 不會刪除分配和分配給a的內存。 (如果您使用了智能指針std::unique_ptr<A> a = std::make_unique<A>();
內存將被釋放,因為std::unique_ptr<A>
的析構函數被調用,它將會釋放記憶。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.