2015-02-05 87 views
12

當我們超出catch塊範圍,異常析構函數被調用? (如果我們不重新拋出它)異常與非虛擬析構函數C++

假設我有類A,並且它的析構函數不是虛擬的。 乙繼承A. 假設一些函數拋出B級的對象,作爲異常, ,它是由一個catch塊

catch(A& a){ 
... 
} 

抓到如果異常析構函數應該被稱爲當外出捕範圍, 在這種情況下只有基類A的析構函數會被調用?

Cornstalks: 現場試用結果調用這兩個類的析構函數。

它違背了我的邏輯。解釋某人?

+1

我對你爲什麼問這個問題有點感興趣;這是一個非常有效的,並非真正基本的問題,但它表明你關心何時調用異常析構函數的時間點,這是你通常不會做的。 – 2015-02-05 20:52:13

+0

嗯,我不確定最後一部分。 – 2015-02-05 20:53:11

+0

@MarcusMüller:想要獲得關於我們使用的工具的知識有什麼問題? – 2015-02-05 20:53:27

回答

5

好的,有人已經回答了你的第一個問題。我將專注於此:

如果應該在超出捕獲範圍時調用異常析構函數,那麼在這種情況下只會調用基類A的判定函數?

實現將始終銷燬異常對象,無論它如何被捕獲。該實現構造了異常對象,所以它知道如何銷燬它。這與您通過指針調用delete時不一樣,因爲在那種情況下,除非存在虛擬析構函數,否則在那一點上關於該對象的完整類型的信息不完整(可能是其他地方的new)。

如果不是這種情況,catch (...)根本就不起作用。

+0

(它可能已經在其他地方被刷新了) - 你能解釋一下嗎? – 2015-02-05 21:22:33

+3

而且,爲了完整性,通過指向基類型的指針刪除指向派生對象的指針,當基類型沒有虛析構函數時,會產生**未定義的行爲**。它可能運行基本的析構函數,但它可能會做一些完全不同的事情。 – 2015-02-05 21:49:39

+0

@Day_Dreamer對不起,這是一句尷尬的句子,但重點是,在代碼中指針爲'delete'd的地方,沒有辦法將它與相同對象的位置相匹配, new'ed。這就是爲什麼你需要虛擬析構函數,否則代碼無法「知道」要調用哪個析構函數。 – Brian 2015-02-05 21:53:47

5

當我們走出catch catch作用域時,異常析構函數被調用了嗎? (如果我們不重新拋出)

是:

[C++11: 15.1/4]:[..]異常對象爲以任何方式異常退出或者最後剩下的活動處理程序後銷燬除了重新拋出之外,或類型std::exception_ptr(18.8.5)中指向異常對象的最後一個對象被銷燬,以較晚者爲準。 [..]


如果異常析構函數應該被稱爲當外出捕範圍,在這種情況下,只有基類的D'TOR會叫什麼名字?

No

#include <iostream> 

struct A 
{ 
    A() { std::cout << "A()"; } 
    A(const A&) { std::cout << "A(const A&)"; } 
    A(A&&) { std::cout << "A(A&&)"; } 
    ~A() { std::cout << "~A()"; } 
}; 

struct B : A 
{ 
    B() { std::cout << "B()"; } 
    B(const B&) { std::cout << "B(const B&)"; } 
    B(B&&) { std::cout << "B(B&&)"; } 
    ~B() { std::cout << "~B()"; } 
}; 

int main() 
{ 
    try { 
     throw B(); 
    } 
    catch (A&) { 
    } 
} 

// Output: A()B()~B()~A() 
+0

其實,你的樣品表明否則。我得到下面的輸出(使用相同的在線編譯器):〜B()〜A() – zdan 2015-02-05 21:08:17

+1

Ahem,Lightness,你的coliru鏈接同時顯示'〜B()'和'〜A()'...這是Cornstalks的回答證實了什麼......只是說' – 2015-02-05 21:11:57

+0

@InnocentBystander:我想不可能判斷'〜B()'是否是_throw-expression_中的暫時,或者是否被忽略(這是合法的),而我們'看到'catch'的輸出。 – 2015-02-05 21:50:10

3

雖然我不是從標準報價,似乎扔B和受涼A&會導致兩個A的和B的析構函數獲取調用。 Live demo

#include <iostream> 

struct A 
{ 
    ~A() { std::cout << "A::~A" << std::endl; } 
}; 

struct B : public A 
{ 
    ~B() { std::cout << "B::~B" << std::endl; } 
}; 

void throwit() 
{ 
    throw B{}; 
} 

int main() 
{ 
    std::cout << "beginning main scope" << std::endl; 

    { 
     std::cout << "beginning inner scope" << std::endl; 

     try 
     { 
      std::cout << "calling throwit()" << std::endl; 
      throwit(); 
     } 
     catch (A& a) 
     { 
      std::cout << "caught exception" << std::endl; 
     } 

     std::cout << "ending inner scope" << std::endl; 
    } 

    std::cout << "ending main scope" << std::endl; 
} 

輸出:

開始主要範圍
開始內部範圍
調用throwit()
捕獲的異常
B ::〜乙
A ::〜甲
結束內部範圍
結束主要範圍

正如你所看到的,這兩個析構函數都會被調用。額外的範圍打印非常清楚地顯示了何時調用析構函數(在catch塊的末尾)。

3

每當標準說某個對象被銷燬時,這意味着調用了正確的最大派生析構函數。

總是。

當你多態刪除對象不虛析構函數,或者您終止(通過delete運營商或明確的析構函數調用)不完全類型的對象,並在適當的析構函數是不平凡的,標準不說,對象被銷燬。它沒有說調用基類析構函數。它說你有未定義的行爲。