2010-02-02 290 views
48

最近我的一位朋友問我如何防止C++中的類繼承。他希望編輯失敗。防止C++中的類繼承

我在想它,找到3個答案。不確定哪一個是最好的。

1)私有構造(多個)

class CBase 
{ 

public: 

static CBase* CreateInstance() 
{ 
    CBase* b1 = new CBase(); 
    return b1; 
} 

private: 

CBase() { } 
CBase(CBase3) { } 
CBase& operator=(CBase&) { } 


}; 

2)使用CSealed基類,私有構造函數&虛擬繼承

class CSealed 
{ 

private: 

CSealed() { 
} 

friend class CBase; 
}; 


class CBase : virtual CSealed 
{ 

public: 

CBase() { 
} 

}; 

3)使用CSealed基類,受保護的ctor &虛擬繼承

class CSealed 
{ 

protected: 

CSealed() { 
} 

}; 

class CBase : virtual CSealed 
{ 

public: 

CBase() { 
} 

}; 

以上所有方法都確保CBase類不能被繼承。 我的問題是:

1)哪種方法最好?還有其他方法嗎?

2)方法2 & 3將不起作用,除非CSealed類被虛構地繼承。這是爲什麼 ?它與vdisp ptr有什麼關係?

PS:

上述程序是在MS C++編譯器(Visual Studio中)編譯。 參考:http://www.codeguru.com/forum/archive/index.php/t-321146.html

回答

-1

還有一個解決辦法:

template < class T > 
class SealedBase 
{ 
protected: 
    SealedBase() 
    { 
    } 
}; 

#define Sealed(_CLASS_NAME_) private virtual SealedBase<_CLASS_NAME_> 


#include "Sealed.h" 

class SomeClass : Sealed(Penguin) 
{ 
}; 
+3

向混合中添加一個宏並不能使它成爲一個新的解決方案。 – 2014-09-04 21:22:51

11

你正在經歷的扭曲,以防止進一步的子類。爲什麼?記錄類不可擴展並使dtor非虛擬的事實。本着C的精神,如果有人真的想忽略你打算使用的方式,爲什麼要阻止它們呢? (我從來沒有在java中看到過類或方法的final)。

//Note: this class is not designed to be extended. (Hence the non-virtual dtor) 
struct DontExtened 
{ 
    DontExtened(); 
    /*NOT VIRTUAL*/ 
    ~DontExtened(); 
    ... 
}; 
+10

我認爲Java中的一點是JIT編譯器可以優化對虛擬方法的調用,如果該類是最終的 – Manuel 2010-02-02 13:39:55

+0

@Manuel。我明白jvm可能會喜歡它,但應該有一種簡單的方法來撤消那些不改變源代碼的功能。 '@ReallyOverride?' – KitsuneYMG 2010-02-02 14:20:31

+1

這是一個側面。在Java中,'final'關鍵字是有意義的,因爲默認情況下所有函數都是虛擬的,所以通過允許JIT編譯器執行這些優化有很多好處。在C++中,從類似的機制中無法獲得防止子類化的東西,這就是爲什麼該語言不提供實現它的mecehanism。 – jalf 2010-02-02 15:54:31

5

1)是一個品味問題。如果我看得很清楚,那麼更多花哨的第二和第三解決方案會在某些情況下從鏈接時間到編譯時間移動錯誤,這通常會更好。

2)需要虛擬繼承來強制將(虛擬)基類初始化爲基類ctor不再可到達的最派生類的責任。

9

您不能阻止繼承(在C++ 11的final關鍵字之前) - 您只能阻止繼承類的實例化。換句話說,沒有辦法預防:

class A { ... }; 

class B : public A { ... }; 

最好的辦法是防止類型B的對象被實例化。既然如此,我建議你接受kts的建議,並記錄A(或其他)不打算用於繼承的事實,給它一個非虛擬的析構函數,並且沒有其他的虛函數,並將它留在那裏。

+1

+1:即使我們(和宇宙的其他部分)可能不同意,你也不能阻止某人選擇繼承而不是使用繼承。記下它 – 2010-02-02 13:54:20

+5

請注意,在C++ 11中,您可以輕鬆阻止繼承。 – 2014-04-12 09:20:54

1

如果可以的話,我會去第一個選項(私人構造函數)。原因在於幾乎所有有經驗的C++程序員都會一眼就能看出,並且能夠識別出您正試圖阻止子類化。

可能還有其他更棘手的方法來防止子類化,但在這種情況下越簡單越好。

4

要回答你的問題,你不能從CBase繼承,因爲在虛擬繼承中,派生類需要直接訪問虛擬繼承的類。在這種情況下,從CBase派生的類將需要直接訪問CSealed,因爲構造函數是私有的,所以它不能。

雖然我沒有看到這一切(即:停止繼承)的用處,你可以概括使用模板(我不認爲它編譯於所有的編譯器,但它確實MSVC)

template<class T> 
class CSealed 
{ 
    friend T; // Don't do friend class T because it won't compile 
    CSealed() {} 
}; 

class CBase : private virtual CSealed<CBase> 
{ 
}; 
+1

它必須是課CBase:私人虛擬CSealed 。否則,可以派生CBase。 – Jagannath 2010-02-02 15:31:06

54

由於C++ 11,你可以final關鍵字添加到您的類,如

class CBase final 
{ 
... 

主要的原因,我可以看到想要做到這一點(我過來找這個問題的原因)是標記一個非子類的類,所以你可以安全地使用一個非虛擬的析構函數並完全避免一個虛表。

+2

還有一個很好的理由,那就是阻止派生類打破不可變類的契約。 – 2014-04-12 09:20:12

+0

@Newjaja Boric將適用於任何子類和任何合同,而不僅僅是可變性。任何一個子類都可能會破壞這個類的任何隱含的合約 - 這並不是一個禁止所有子類的好理由。對於一個不可變的對象,如果你想添加一個派生值,例如來自FirstName()和LastName()方法的FullName(),或者可能是一個特定的散列函數。 – 2014-04-16 08:24:58

0
class myclass; 

    class my_lock { 
     friend class myclass; 
    private: 
     my_lock() {} 
     my_lock(const my_lock&) {} 
    }; 

    class myclass : public virtual my_lock { 
     // ... 
    public: 
     myclass(); 
     myclass(char*); 
     // ... 
    }; 

    myclass m; 

    class Der : public myclass { }; 

    Der dd; // error Der::dd() cannot access 
      // my_lock::my_lock(): private member 

我在這裏找到了信用。我在這裏發帖只是其他人可以輕鬆地訪問 http://www.devx.com/tips/Tip/38482

0

爲了詳細說明Francis' answer:如果類BottomMiddle類,這無形中從Top類繼承派生,它是最派生類(Bottom),負責建設虛擬繼承的基類(Top)。否則,在多繼承/死亡鑽石的情況下(虛擬繼承經常使用),編譯器不會知道兩個「中間」類中哪些應該構造單個基類。當Middle正在從Bottom構建的Middle的構造函數的構造函數的對Top呼叫「因此忽略:

class Top { 
    public: 
     Top() {} 
} 

class Middle: virtual public Top { 
    public: 
     Middle(): Top() {} // Top() is ignored if Middle constructed through Bottom() 
} 

class Bottom: public Middle { 
    public: 
     Bottom(): Middle(), Top() {} 
} 

因此,在接近2)或3)在你的問題,Bottom()不能打電話Top(),因爲它在Middle中是私下繼承的(默認情況下,如代碼中那樣,但值得明確),因此在Bottom中不可見。 (source