2009-08-07 42 views
15

假設我有一個名爲「Base」的類和一個名爲「Derived」的類,它是Base的子類並訪問受保護的方法和Base成員。有沒有辦法禁止我班的子類?

我現在要做的就是讓其他類不能繼承Derived。在Java中,我可以通過聲明Derived類爲「final」來完成此操作。是否有一些C++技巧可以給我提供相同的效果? (理想情況下,我想這樣做,除Derived之外的其他類都可以繼承Base。我不能只將所有代碼放入同一個類或使用friend關鍵字,因爲Base和Derived是這兩個模板,具有更少的模板參數比基礎衍生確實....)

+0

我會包含一些源代碼 - 因爲它涉及到模板。 – sylvanaar 2009-08-07 21:44:31

+0

如果一個類沒有虛擬析構函數,那麼你可能不應該從這個虛擬析構函數中刪除它(這只是一個好的編程)。 C++認識到有時你需要擴展邊界的能力,並讓你從中繼承。所以你問題不是一個教育的語言。 – 2009-08-08 01:00:03

+0

投票:刪除關鍵字/最終標籤,但添加** derived-class **標籤代替 – fmuecke 2009-12-08 11:03:14

回答

12

由於C++ 11,你可以在最後加上關鍵字(技術上是一個特殊的標識符,因爲它實際上不是一個關鍵字)到您的類,如

class Derived final 
{ 
... 

你可以閱讀更多關於final關鍵字在http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

+0

+1有趣的是,使用C++ 11的解決方案。一個更詳細的描述的額外鏈接,將使這個答案更有幫助。 – Wolf 2014-02-27 08:23:55

9

你可以有一個私人的構造爲「Derived」和一個公共靜態的實例創建函數

5

以禁止子類最簡單的方法是通過使構造函數私有:

class Foo 
{ 
private: 
    Foo() {} 

public: 
    static Foo* CreateFoo() { return new Foo; } 
}; 

編輯:感謝Indeera的指出,這需要一個靜態工廠方法

+0

啊,但你會如何創建Foo的實例? :) – Indy9000 2009-08-07 21:42:40

+0

Foo.CreateFoo() – 2009-08-08 10:55:57

+1

其實'Foo :: CreateFoo()',雖然它爲什麼返回一個指針對我來說是一個謎,因爲類可以完美地被複制... – 2009-12-08 10:40:52

3

有沒有簡單和乾淨的方式來做到這一點。

標準庫所做的只是使析構函數非虛擬化。這並不妨礙子類化,但它對用戶來說是一個強烈的信號,它不是爲繼承而設計的,這意味着在使用派生類時必須非常小心。

最終,雖然,你是否需要絕對使子類化不可能?說明「從這個班級中學習是一個壞主意」是不是很好?

如果他們真的想要,人們總是可以破壞你的代碼。最好的辦法是讓他們意識到他們應該做什麼,不應該做什麼,並希望他們不會主動嘗試來破壞你的代碼。

保護你的代碼免受墨菲而不是馬基雅維利。 ;)

+0

如果您忽略了這個「強烈信號」,是否有工具會發出警告? – Wolf 2014-02-24 14:35:52

1

由於您使用的模板,我在想,你對防止超過來源於基地繼承其他任何一類問題的最後一部分可以用適當的部分特例來完成。

下面的代碼片段是我想出來的,但所需的複雜性只能通過jalf來加強答案。這值得麼?如果這有助於我理解部分專業化,而不是制定一種我將在實踐中使用的技術。

我用普通的指示基類和派生和EXTRA表示,你說派生有額外的參數之間的共享模板參數。這些的實際數量可能是我剛剛碰巧分別挑選了一個和兩個的任何數量。

// Forward declaration of class Derived 
template< class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Derived; 


// Definition of general class template Base 
template< class SUBCLASS 
     , class COMMON > 
class Base 
{ 
private: 
    Base() {} 
}; 


// Definition of partial specialisation of template class Base to open up 
// access to the constructor through friend declaration. 
template< class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Base< Derived< COMMON, EXTRA1, EXTRA2 > 
      , COMMON > 
{ 
private: 
    Base() {} 

    friend class Derived< COMMON, EXTRA1, EXTRA2 >; 
}; 


// Definition of class Derived 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Derived 
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    static Derived* create() { return new Derived; } 

private: 
    Derived() : Base< Derived< COMMON, EXTRA1, EXTRA2 > 
        , COMMON >() 
    { 
    } 
}; 


// Definition of class HonestDerived. 
// It supplies itself as the SUBCLASS parameter to Base. 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class HonestDerived 
    : public Base< HonestDerived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    HonestDerived() : Base< HonestDerived< COMMON, EXTRA1, EXTRA2 > 
          , COMMON >() 
    { 
    } 
}; 


// Definition of class DishonestDerived 
// It supplies Derived rather than itself as the SUBCLASS parameter to Base. 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class DishonestDerived 
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    DishonestDerived() : Base< Derived< COMMON, EXTRA1, EXTRA2 > 
          , COMMON >() 
    { 
    } 
}; 


template< class COMMON, class EXTRA1, class EXTRA2 > 
class DerivedFromDerived 
    : public Derived< COMMON, EXTRA1, EXTRA2 > 
{ 
public: 
    DerivedFromDerived() : Derived< COMMON, EXTRA1, EXTRA2 >() 
    { 
    } 
}; 

// Test partial specialisation gives Derived access to the Base constructor 
Derived< int, float, double >* derived 
    = Derived< int, float, double >::create(); 

// Test that there is no access to the Base constructor for an honest subclass 
// i.e. this gives a compiler error 
HonestDerived< int, float, double > honestDerived; 

// Test that there is no access to the Base constructor for a dishonest subclass 
// i.e. this gives a compiler error 
DishonestDerived< int, float, double > dishonestDerived; 

// Test that there is no access to the Derived constructor 
// i.e. this gives a compiler error 
DerivedFromDerived< int, float, double > derivedFromDerived; 

這段代碼是用gcc 4.3.2測試的。

注意,對朋友聲明的替代方案是使基地的部分特保護的構造函數,但隨後將允許像DishonestDerived類的工作。