web-dev-qa-db-ja.com

C ++でシールドクラスを定義する方法

他のクラスに継承されるクラスを停止する方法。

52
Mahantesh

C++ 11ソリューション

C++ 11では、次のように定義でfinalキーワードを使用してクラスをシールできます。

class A final  //note final keyword is used after the class name
{
   //...
};

class B : public A  //error - because class A is marked final (sealed).
{                   //        so A cannot be derived from.
   //...
};

Finalの他の用途を知るには、ここで私の答えを見てください:


C++ 03ソリュ​​ーション

Bjarne Stroustrupのコードクラスから派生する人々を停止できますか?

class Usable;
class Usable_lock {
    friend class Usable;
private:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};

class Usable : public virtual Usable_lock {
public:
    Usable();
    Usable(char*);
};
Usable a;

class DD : public Usable { };

DD dd;  // error: DD::DD() cannot access
        // Usable_lock::Usable_lock(): private  member

Generic_lock

したがって、テンプレートを使用してUsable_lockすべてのクラスを封印するのに十分な汎用性:

template<class T>
class  Generic_lock 
{
    friend T;
    Generic_lock() {}                     //private
    Generic_lock(const Generic_lock&) {}  //private
};

class Usable : public virtual Generic_lock<Usable>
{
public:
    Usable() {}
};

Usable a; //Okay
class DD : public Usable { };

DD dd; //Not okay!
84
Nawaz

シンプルで安価な方法と正しい方法の2つの方法があります。 @Naveenと@Nawazの2つの答えは正しいものを扱っています。これは、実際にシールしたいクラスごとにsealerクラスを手動で作成する必要があります。

アドビのライブラリで使用されている絶対確実な方法は、そのためにテンプレート化されたクラスを使用しています。問題は、テンプレート引数をフレンドとして宣言できないことです。つまり、privateから安全性の低いprotectedに切り替える必要があります。

template <typename T>
class sealer {
protected: sealer() {}
};
class sealed : virtual sealer<sealed> {};

また、マクロを使用して自動化することもできます(Adobeのコードでマクロの正確なフレーバーを覚えていません)。

#define seal( x ) virtual sealer<x>
class sealed : seal(sealed) 
{};

これで、誤って継承しようとする人が、自分がすべきではないことを知らずにキャッチしようとします。

class derived : sealed {};
int main() {
   derived d;  // sealer<T>::sealer() is protected within this context
}

しかし、テンプレート自体から派生することでコンストラクターにアクセスできるため、実際にが派生したい人を阻害することはありません。

class derived : sealed, sealer<sealed> {};
int main() {
   derived d;
};

これがC++ 0xで変わるかどうかはわかりませんが、クラステンプレートがその引数の1つと友達になることを許可するかどうかについてのいくつかの議論を思い出すと思いますが、ドラフト全体の大まかな検索では私は本当に伝えることができません。それが許可された場合、これは素晴らしい汎用ソリューションになります。

template <typename T>
class sealer {
   sealer() {}
   friend class T; // Incorrect in C++03
};

C++ 11には、クラスからの継承を防止する機能、または派生クラスのメソッドのオーバーライドを防止する機能が追加されています。これは特別な識別子finalで行われます。例えば:

class Base final { };

class Derived1 : Base { }; // ill-formed because the class Base has been marked final

または

class Base {
    virtual void f() final;
};

class Derived : Base {
    void f(); // ill-formed because the virtual function Base::f has been marked final

Finalは言語キーワードではないことに注意してください。技術的には識別子です。これらの特定のコンテキストで使用された場合にのみ、特別な意味を持ちます。その他の場所では、有効な識別子にすることができます。

8
AzP

Bjarne Stroustrupの http://www.stroustrup.com/bs_faq2.html#no-derivation FAQフレンドキーワードを使用せずに小さな変更を加えたものに基づく:

// SEALED CLASS DEFINITIONS
class Usable_lock {
protected:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};
#define sealed_class private virtual Usable_lock

// SEALED CLASS USAGE EXMAPLES
class UsableLast : sealed_class {
public:
    UsableLast(){}
    UsableLast(char*){}
};
class DD : public UsableLast {};

// TEST CODE
template <class T> T createInstance() {
    return T();
}
int main()
{
    createInstance<UsableLast>();
//  createInstance<DD>();
    return 0;
}
0
bruziuz