web-dev-qa-db-ja.com

C ++の静的抽象メソッド

私は抽象的な基本クラスを持っています

class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
};

私が言いたいのは、「具体的なインスタンス化を提供するすべてのクラスは、これらの静的メソッドも提供する必要がある」ということです。

やりたくなります

class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
  static virtual IThingy Factory() = 0;
};

私はそれがコンパイルされないことを知っています、そしてとにかくそれがコンパイルされたとしてもそれを使用する方法が明確ではありません。そしてとにかく私はただすることができます

Concrete::Factory(); // concrete is implementation of ITHingy

基本クラスのファクトリについてはまったく触れません。

しかし、実装にサインアップしてほしい契約を表現する方法があるはずだと思います。

これについてよく知られているイディオムはありますか?それともコメントに入れるだけですか?多分私はとにかくこれを強制しようとすべきではありません

編集:質問を入力すると、漠然としているように感じることがありました。それを表現する方法があるべきだと感じました。イゴールはエレガントな答えを出しますが、実際にはそれは本当に役に立たないことを示しています。私はまだしなければならないことになります

   IThingy *p;
    if(..)
       p = new Cl1();
    else if(..)
       p = new Cl2();
    else if(..)
       p = new Cl3();
    etc.

C#、pythonまたはJavaのような反射言語は、より良い解決策を提供できると思います

22
pm100

あなたが抱えている問題は、単一責任の原則にわずかに違反することに部分的に関係しています。インターフェイスを介してオブジェクトの作成を強制しようとしていました。代わりに、インターフェースはより純粋で、インターフェースが行うことになっていることに不可欠なメソッドのみを含む必要があります。

代わりに、作成物をインターフェイス(目的のvirtual staticメソッド)から取り出して、ファクトリクラスに入れることができます。

これは、派生クラスにファクトリメソッドを強制する単純なファクトリ実装です。

template <class TClass, class TInterface>
class Factory {
public:
    static TInterface* Create(){return TClass::CreateInternal();}
};

struct IThingy {
    virtual void Method1() = 0;
};

class Thingy : 
    public Factory<Thingy, IThingy>,
    public IThingy {
        //Note the private constructor, forces creation through a factory method
        Thingy(){}
public:
        virtual void Method1(){}
        //Actual factory method that performs work.
        static Thingy* CreateInternal() {return new Thingy();}
};

使用法:

//Thingy thingy; //error C2248: 'Thingy::Thingy' : cannot access private member declared in class 'Thingy'

IThingy* ithingy = Thingy::Create(); //OK

Factory<TClass, TInterface>から派生させることにより、派生クラスはコンパイラーによってCreateInternalメソッドを持つように強制されます。定義しないと、次のようなエラーが発生します。

エラーC2039:「CreateInternal」:「Thingy」のメンバーではありません

33
Igor Zevaka

この種のポリモーフィズムを使用する方法もないため、C++でそのようなコントラクトを規定する確実な方法はありません。

Concrete::Factory()

は常に静的なコンパイル時のものです。つまり、Concreteがまだ不明なクライアント提供のクラスであるこの行を記述することはできません。

この種の「契約」を提供しないよりも便利にすることで、クライアントにこの種の「契約」を実装させることができます。たとえば、CRTPを使用できます。

class IThingy {...};

template <class Derived>
class AThingy : public IThingy
{
public:
  AThingy() { &Derived::Factory; } // this will fail if there is no Derived::Factory
};

そして、クライアントにAThingy<their_class_name>から派生するように指示します(コンストラクターの可視性を微調整することでこれを強制できますが、クライアントがtheir_class_nameについて嘘をつかないようにすることはできません)。

または、従来のソリューションを使用して、ファクトリクラスの個別の階層を作成し、クライアントにConcreteFactoryオブジェクトをAPIに提供するように依頼することもできます。

0
jpalecek

静的メソッドは、C++では仮想(または抽象)にすることはできません。

意図したことを実行するには、具体的なインスタンスを返す_IThingy::factory_メソッドを使用できますが、ファクトリがインスタンスを作成するための手段を何らかの方法で提供する必要があります。たとえば、IThing* (thingy_constructor*)()のようなメソッドシグネチャを定義し、IThingyに静的呼び出しを設定して、IThingyがファクトリインスタンスを構築する方法を定義する関数を渡すことができます。次に、依存ライブラリまたはクラスで、適切な関数を使用してこのメ​​ソッドを呼び出すことができます。これにより、インターフェイスを実装するオブジェクトを適切に構築する方法がわかります。

ファクトリの「初期化子」が呼び出されていない場合は、例外をスローするなど、適切なアクションを実行する必要があります。

0
Nathan Ernst