2011-06-30 81 views
8
class A 
{ 
    public: 
    virtual ~A() 
    { 
    } 
}; 

class B : virtual public A 
{ 
    public: 
    ~B() throw() 
    {} 
}; 

class C : public B 
{ 
}; 

int main(int argc, char * argv []) 
{ 
return 0; 
} 

該代碼提供了以下錯誤:GCC異常規範

error: looser throw specifier for ‘virtual C::~C()’ 
error: overriding ‘virtual B::~B() throw()’ 

在我的Debian測試(GCC(Debian的4.6.0-10)4.6.1 20110526(搶鮮)),但編譯在以前的gcc版本上沒有錯誤(再次在我的debian系統上使用4.5)。

How does an exception specification affect virtual destructor overriding? 根據這個答案,編譯器應該創建一個匹配基類的throw聲明的默認構造函數。很明顯,這不是新gcc上發生的事情。什麼改變了,什麼是正確的編譯器行爲,除了在派生類中手動添加空的析構函數(例如編譯器標誌)之外,還有一些簡單的解決方案。

+0

這是實際的代碼嗎?如果你跳過了一個成員聲明,​​那可以改變結果。另外,您是否指定了C++ 11支持?規則稍微改變了析構函數,雖然代碼仍然可以,但在某處可能會有錯誤。 –

+4

不,這不是實際的代碼。我可以說,因爲它至少有一個語法錯誤。 @Yordan,請在提問時發佈實際的,可編譯的代碼。有關如何做到這一點的細節以及爲什麼它很重要,請參閱http://sscce.org。 –

+0

@Rob - O.o你一定是在開玩笑..?這不是一個複雜而又冗長的源代碼的問題,這些東西很重要。兩個明顯的分號丟失,這不會改變問題,信息,環境,任何事情。 –

回答

3

我相信,在真正的代碼,通過~A()~B()被宣佈爲虛擬? (錯誤消息是抱怨虛擬析構函數,但在代碼中寫入的任何析構函數都是虛擬的。)

我相信虛擬繼承是引發你的問題的原因。 C的(隱式定義的)析構函數需要首先調用~B(),然後,因爲C是派生類最多的類,所以需要調用~A()。 (12.4/6)

生成的異常規範~C()需要允許傳播任何異常,因爲它直接調用~A(),它沒有異常規範。 (15.4/13)

然後,這會觸發你的錯誤 - 你不能用一個可能拋出的版本來覆蓋一個帶有throw()規範(B的析構函數)的虛擬函數。 (15.4/3)

解決方法是將throw()放在A的析構函數上。 (如果你不能這樣做,那你爲什麼要在B上做它?)

如果沒有虛擬繼承,錯誤也不會發生 - 因爲那麼C的析構函數只會調用B的析構函數。 (B的析構函數仍然會調用A的 - 而且你仍然在薄冰上滑冰,因爲如果A的析構函數拋出你直接去terminate()。)

+0

謝謝你的回答!你是對的關於A有一個我錯過的虛擬引誘器(我現在在這個例子中糾正了這個問題)看起來不像你提供的解決方案,向A添加throw()對我來說是正確的 –

+0

更好的解決方案是不要使用'throw'規範。這個規範現在被廣泛認爲是該語言中的一個錯誤。 –

0

GCC 4.X比以前的版本更嚴格,因此可能不會隱式聲明。嘗試明確說明它。

我的理解是,如果B類有析構函數,其中明確扔什麼的方式(即

class B: public A 
{ 
public: 
virtual ~B() throw { } 
} 

應該差不多。

無論如何,我最後一次檢查是實踐很差從D'職責範圍拋出異常。

希望幫助雅一些!

+0

宣佈異常規範以及 –

+0

「這是一個不好的做法」除了在派生類**中手動添加空析構函數之外,是否還有一些簡單的解決方法?「。在析構函數中添加'throw()'(注意 - 它是_empty_ throw說明符(類似於C++ 0x中的nothrow))與僅使用throw說明符不同。這不是一個壞習慣。甚至建議 - 破壞者_不應該扔,對吧? (: –

+0

我不確定它是否可能或不可以..但是據我所知,這些東西不應該被扔掉,這意味着每次你在一次嘗試中刪除一個對象/ catch。我會說:不 - 但是這看起來像濫用投擲機制給我,所以我不能肯定地說,當你以一種方式反對另一種方式時會發生什麼情況,儘管不是,在閱讀OP的摘錄之後如果沒有指定與基類覆蓋函數相同的異常類型,就無法做到這一點。 我認爲這與指針類型不明確有關 – Syndacate