2012-09-17 82 views
12

我明白,與公共繼承它一般不是安全,因爲當delete ing一個基類指針編譯器只生成代碼來調用基類的析構函數,並且派生類的一個不被調用。使用非虛擬析構函數從類中私下繼承是否安全?

但是對於私有繼承的客戶端無法派生類指針轉換爲一個基類指針(如私有繼承不模型是,一個關係),所以delete總是用在派生類的指針,編譯器應該能夠看到還有一個基類並調用它的析構函數。

我做了這個測試:

#include <iostream> 

struct BaseVirtual 
{ 
    virtual ~BaseVirtual() 
    { 
     std::cout << "BaseVirtual's dtor" << '\n'; 
    } 
}; 

struct BaseNonVirtual 
{ 
    ~BaseNonVirtual() 
    { 
     std::cout << "BaseNonVirtual's dtor" << '\n'; 
    } 
}; 

struct DerivedPrivVirtual: private BaseVirtual 
{ 
    static void f() 
    { 
     BaseVirtual * p = new DerivedPrivVirtual; 
     delete p; 
    } 

    ~DerivedPrivVirtual() 
    { 
     std::cout << "DerivedPrivVirtual's dtor" << '\n'; 
    } 
}; 

struct DerivedPrivNonVirtual: private BaseNonVirtual 
{ 
    static void f() 
    { 
     BaseNonVirtual * p = new DerivedPrivNonVirtual; 
     delete p; 
    } 

    ~DerivedPrivNonVirtual() 
    { 
     std::cout << "DerivedPrivNonVirtual's dtor" << '\n'; 
    } 
}; 

int main() 
{ 
    std::cout << "With explicit derived pointer type:" << '\n'; 
    { 
     DerivedPrivVirtual * derivedPrivVirtual = new DerivedPrivVirtual; 
     DerivedPrivNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual; 

     delete derivedPrivVirtual; 
     delete derivedPrivNonVirtual; 
    } 
    std::cout << '\n'; 

    std::cout << "With base pointer type:" << '\n'; 
    { 
     // Client code can't cast Derived to Base when inherit privately. 
     //BaseVirtual * derivedPrivVirtual = new DerivedPrivVirtual; 
     //BaseNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual; 

     //delete derivedPrivVirtual; 
     //delete derivedPrivNonVirtual; 
    } 
    std::cout << '\n'; 

    std::cout << "Inside derived class itself:" << '\n'; 
    { 
     DerivedPrivVirtual::f(); 
     DerivedPrivNonVirtual::f(); 
    } 
    std::cout << '\n'; 

    std::cout << "With non-dynamic variables:" << '\n'; 
    { 
     DerivedPrivVirtual derivedPrivVirtual; 
     DerivedPrivNonVirtual derivedPrivNonVirtual; 
    } 
    std::cout << '\n'; 
} 

兩個GCC 4.7.1和3.1鏗鏘給出相同的輸出。派生類的構造函數被調用,除非派生類本身將派生類指針轉換爲基類並且將其指向基類。

除了這種情況下,這種情況似乎很少見,也很容易避免(班級的作者是唯一能夠傷害的人,但它確實知道從哪個班級衍生出來),我可以得出結論:這是安全的嗎?

With explicit derived pointer type: 
DerivedPrivVirtual's dtor 
BaseVirtual's dtor 
DerivedPrivNonVirtual's dtor 
BaseNonVirtual's dtor 

With base pointer type: 

Inside derived class itself: 
DerivedPrivVirtual's dtor 
BaseVirtual's dtor 
BaseNonVirtual's dtor <-- Only a problem inside the class itself 

With non-dynamic variables: 
DerivedPrivNonVirtual's dtor 
BaseNonVirtual's dtor 
DerivedPrivVirtual's dtor 
BaseVirtual's dtor 

獎金的問題:關於保護繼承什麼?我認爲造成傷害的能力不再是直接派生類作者的特權,而是階級中任何階級的作者。

+0

如果我沒有記錯,斯科特邁爾斯(有效C++的作者,更有效的C++)仍然不知道什麼保護繼承的含義。公衆是一個「是 - 是」的關係,私人是「實施的」,但受到保護?這有點嚇人。 – Borgleader

回答

9

無論繼承的公共或私人不影響代碼的安全性,它只是限制了它可以安全/使用不安全的範圍。你有相同的基本問題:如果你的類或類的朋友通過你的類型的對象,需要一個指向基不虛析構函數的接口,如果該接口獲取你的對象的所有權,那麼你正在創建未定義行爲。

設計的問題是,根據您的問題,BaseNonVirtual沒有設計可以延長。如果是這樣,它應該有一個公共虛擬析構函數或一個受保護的非虛擬析構函數,以確保沒有代碼能夠通過指向基的指針調用對派生對象的刪除。

+0

任何構造都可能被濫用。宣佈私人數據成員是否安全?哦,不,你可以不小心將它設置在哪裏不aporopriate! 「安全」通常意味着「不會被他人濫用」,私人繼承與私人數據成員一樣安全。 –

+0

@牛米:如果你仔細閱讀,你應該注意,在提到此結構將失敗的情況下,第一款:*如果你的類[..]將類型的對象傳遞給... *。它還提到第二段中解決這個問題的辦法。私有繼承與私有數據成員一樣安全,只要您不會產生指向成員的指針,但這種情況並不常見。另一方面,繼承常常用於指向基礎 - 如果繼承是(ab)用於獲得不同設計問題的功能。 –

0

還有就是客戶端代碼可以儘管私有繼承投推導基本情況:

delete reinterpret_cast<BaseNonVirtual*>(new DerivedPrivNonVirtual);

~DerivedPrivNonVirtual()因此跳過執行。

但是,給多少使用reinterpret_cast氣餒,你可能認爲它是「足夠安全」爲您的目的。