2009-08-07 17 views
15

「Base」というクラスと「Base」のサブクラスである「Derived」というクラスがあり、保護されたメソッドやBaseのメンバーにアクセスするとします。私のクラスのサブクラス化を禁止する方法はありますか?

私が今したいことは、他のどのクラスもDerivedをサブクラス化できないようにすることです。 Javaでは、Derivedクラスをfinalと宣言することでこれを実現できます。私に同じ効果を与えることができるC++のトリックがありますか?

(理想的には、Derived以外のクラスでもBaseをサブクラス化できないようにしたいのですが、BaseとDerivedが同じクラスにすべてのコードを入れることはできません。両方

+0

テンプレートが含まれているので、私はいくつかのソースコードを含めることにします。 – sylvanaar

+0

クラスに仮想デストラクタがない場合は、おそらくそれを使っていないはずです。 C++では、境界線をストレッチする能力が必要な場合があり、とにかにそれを継承することができることを認識しています。だから問題は、教育的なものを置く言語ではない。 –

+0

投票:キーワード/最終タグを削除しますが、代わりに**派生クラス**タグを追加してください – fmuecke

答えて

12

(それは実際にはキーワードではありませんので、技術的に特別な識別子)、あなたのクラスに、最終的なキーワードを追加することができますに、例えば

class Derived final 
{ 
... 

あなたは、最終的なキーワードの詳細を読むことができますhttp://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

+0

+1このソリューションをC++で使用するのは面白いです。より詳細な説明へのリンクがあれば、この回答はさらに役立ちます。 – Wolf

9

あなたは「Derived」のプライベートコンストラクタおよびパブリック静的インスタンス化のための関数を作成することができます)....ベースがない派生より少ないテンプレート引数を持つ、テンプレート

5

サブクラスを禁止する最も簡単な方法はありますコンストラクタを非公開にすることによって:

class Foo 
{ 
private: 
    Foo() {} 

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

編集:これはそれを行うには、シンプルでクリーンな方法はありませんstaticファクトリメソッド

+0

ああ、どうやってFooのインスタンスを作成しますか? :) – Indy9000

+0

Foo.CreateFoo() –

+1

実際に 'Foo :: CreateFoo()'は、なぜクラスが完全にコピーできるので、ポインタが私には謎であるのですか? –

3

を必要としていることを指摘しIndeeraに感謝します。

標準ライブラリは単にデストラクタを非仮想化するだけです。これはサブクラス化を防ぎませんが、継承のために設計されていないことをユーザーに強く伝えます。つまり、派生クラスを使用する際には非常に注意する必要があります。

最終的には、が必要ですを絶対にサブクラス化することはできません? 「このクラスから派生することは悪い考えである」ということを示すことは十分ではありませんか?

本当に望むなら、人々はいつもあなたのコードを壊すことができます。あなたができることは、何をすべきか、またはすべきでないかを意識させることです。そして、彼らが積極的に役に立たないことを願ってください。あなたのコードを壊してみてください。

マキアベリではなく、マーフィーに対してコードを保護します。 ;)

+0

この「強力な信号」を見落とすかどうかを警告するツールはありますか? – Wolf

1

テンプレートを使用しているので、私は、Derived以外のクラスをBaseからサブクラス化することについての質問の最後の部分は、適切な部分的な特殊化を使用して行うことができると考えていました。

次のコードスニペットは私が思いついたものですが、複雑さが必要なのはjalfの答えを強化するためだけです。その価値はありますか?何かがあれば、実際に使っていた技術を開発するよりも、部分的な専門化を理解するのに役立ちました。

私はCOMMONを使用して、BaseとDerivedの間の共有テンプレートパラメータを示し、EXTRAを使用して、Derivedが持つ追加のパラメータを示します。これらの実際の数は、ちょうど私がこれらのためにそれぞれ1つと2つを選んだことがあります。

// 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でテストされています。

フレンド宣言の代わりに、Baseの部分的な特殊化でコンストラクタを保護することができますが、DishonestDerivedなどのクラスが動作するようにすることができます。C++ 11のよう

関連する問題