簡體   English   中英

從構造函數拋出異常時,為什么會出現內存泄漏?

[英]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.

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