2017-04-07 63 views
3

我有一個純粹的抽象接口類和一個實現該接口的派生類。使用受保護的非虛擬析構函數時,禁止刪除非虛擬dtor警告

struct Foo 
{ 
    virtual void doStuff() = 0; 
}; 

struct Bar : Foo 
{ 
    void doStuff() override { } 
}; 

我的接口類沒有虛析構函數

試圖使用一個基類指針破壞派生實例明顯,因此不確定的行爲

int main() 
{ 
    Foo* f = new Bar; 
    f->doStuff(); 
    delete f; 
} 

幸運的是我的編譯器是聰明地抓住這個(與-Werror

main.cc:15:9: error: deleting object of abstract class type ‘Foo’ which has 
    non-virtual destructor will cause undefined behaviour [-Werror=delete-non-virtual-dtor] 
delete f; 
     ^

我可以通過確保我不試圖使用基類指針來刪除這個未定義的行爲

int main() 
{ 
    Bar* b = new Bar; 
    b->doStuff(); 
    delete b; 
} 

可惜這不是足夠聰明,拿起這個程序是良好的,並吐出了一個類似的錯誤

main.cc:15:9: error: deleting object of polymorphic class type ‘Bar’ which has 
    non-virtual destructor might cause undefined behaviour [-Werror=delete-non-virtual-dtor] 
    delete b; 
    ^

有趣的是,它說威力導致不確定的行爲,而不是

受保護的非虛擬析構函數:

one of Herb Sutter's Guru of the Week's,他給出了以下建議:

方針#4:基類的析構函數應該是公開和虛擬,或保護和非虛。

因此,讓我的析構函數保護nonvirtual。

struct Foo 
{ 
    virtual void doStuff() = 0; 
protected: 
    ~Foo() = default; 
}; 

struct Bar : Foo 
{ 
    void doStuff() override { } 
}; 

現在,當我不小心嘗試使用基類指針刪除我得到另一個失敗

int main() 
{ 
    Foo* f = new Bar; 
    f->doStuff(); 
    delete f; 
} 
main.cc:5:2: error: ‘Foo::~Foo()’ is protected 
    ~Foo() = default; 
^
main.cc:17:9: error: within this context 
    delete f; 
     ^

大,這給了我什麼,我一直在尋找。讓我們來修復代碼,所以我不刪除使用一個基類指針

int main() 
{ 
    Bar* b = new Bar; 
    b->doStuff(); 
    delete b; 
} 

不幸的是我得到了相同的錯誤之前

main.cc:17:9: error: deleting object of polymorphic class type ‘Bar’ which has 
non-virtual destructor might cause undefined behaviour [-Werror=delete-non-virtual-dtor] 
    delete b; 
     ^

問:

如何我可以獲得兩全其美嗎?

  • 保持delete-non-virtual-dtor錯誤時,我忘記了建立一個保護非虛析構函數,我試圖通過一個基類指針
  • 當我使用保護非虛析構函數禁止警告刪除和我通過一個派生類指針

超級真棒獎金額外刪除:

  • 禁止的警告時,我忘了使用保護非虛析構函數,但我通過派生類指針正確刪除
+0

你能把'Bar'標記爲'final class'嗎? – Jarod42

+0

警告似乎是與編譯器相關的。您可能希望將編譯器標籤添加到帖子中。 –

+0

@ Jarod42我可以爲我的層次中的一些類,但不幸的是不是所有的 –

回答

1

編譯器告訴你,問題是出在酒吧沒有。如果你有從酒吧繼承另一個類巴茲說:

struct Baz : public Bar 
{ 
    void doStuff() override { } 
}; 

這可能會導致不確定的行爲,如情況

int main() 
{ 
    Bar* bar_ptr = new Baz(); 
    bar_ptr->do_stuff(); 
    delete bar_ptr; // uh-oh! this is bad! 
} 

因爲酒吧析構函數不是虛擬的。因此,解決方案是將Bar標記爲最終建議,或者將Bar中的析構函數虛擬化(因爲它是公開的),或者根據Herb的建議進行保護。

+0

如果我*不能*使它成爲'final',這可能意味着我打開了一個用戶子類的選項,因此*要求*析構函數是虛擬的。無論如何,謝謝 - 這已經說明了我還沒有加入的問題的缺失部分 –

1

標記類final刪除警告。

struct Bar final : Foo 
{ 
    void doStuff() override { } 
}; 

int main() 
{ 
    Bar* f = new Bar; 
    f->doStuff(); 
    delete f; 
} 

Demo

+0

不幸的是,這不是一個通用的解決方案,我的使用案例 –