1

回答 Johannes Schaub's answerに触発されて、リフレクティブファクトリを実装しようとしました。アイデアは、ヒープ上に関連するオブジェクトを作成し、共通の基本クラスへのポインターを返すメソッドにクラスの名前を渡すことにより、オブジェクトを作成できるということです。必要に応じて、dynamic_cast と RTTI を使用して、オブジェクトを元の型に戻すことができます。以下に示すように、反射ファクトリを使用できるはずです。

// Base is the base class of all objects to create.
class Factory: public AbstractFactory<Base>
{
public:
    Factory()
    {
        m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;
        m_map["DerivedB"] = &AbstractFactory::createInstance<DerivedB>; 
    }
};

int main()
{
    Factory factory;
Base *a = factory.Create("DerivedA");
DerivedA *aa = dynamic_cast<DerivedA*>(a);

    // etc..
}

私はこれまでのところこれを機能させました。ただし、以下のコードには 2 つの主な問題があります。それは醜く、抽象ファクトリのメソッドを保護すると、メソッドにアクセスできないと不平を言いますcreateInstance()。これは私が理解していないものです。定義により、派生クラスは基本クラスの保護されたメソッドにアクセスできる必要があります。私は VS 2008 でコードをテストしました。追加メモ: 純粋な仮想関数が含まれていないため、基本クラスが実際には抽象的ではないことはわかっています。std::function については知っていますが、これまで使用していません。多分私は将来そうするでしょう。

#include <iostream>
#include <map>
#include <typeinfo>
#include <string>

struct Base
{
    virtual std::string SayHello() = 0;
};

struct DerivedA: public Base
{

    virtual std::string SayHello() 
    {
        return "Hello from DerivedA";
    }

};

struct DerivedB: public Base
{

    virtual std::string SayHello() 
    {
        return "Hello form DerivedB";
    }

};

/**
 * @brief Reflective Factory class. Creates objects of classes which derive from
 *        a common base class.
 *
 */
template<class BASE_T>
struct AbstractFactory
{

// Macro to call ptrs to member functions as recommended in the C++ FAQ lite.
// http://www.parashift.com/c++-faq-lite/pointers-to-members.html
#define CALL_MEMBER_FN(object, ptrToMember) ((object).*(ptrToMember))

    // Recall funcion ptr syntax for members: ReturnType (class::*) (Arguments)

    // using a typedef makes it easier..
    typedef BASE_T*  (AbstractFactory::*func_ptr_type) ();
    // retType^ ClassName^ AliasName^  Arguments^

    typedef std::map<std::string, func_ptr_type> map_type;

    template<typename DERIVED_T> 
    BASE_T * createInstance() 
    { return new DERIVED_T; }

    map_type m_map;

    /**
     * @brief Creates an object from a class with the name given as string.             
     */
    BASE_T * Create(std::string className)
    {
        // Note the last () at the end.
        return CALL_MEMBER_FN(*this, m_map[className])();
    }

#undef CALL_MEMBER_FN

};

class Factory: public AbstractFactory<Base>
{

public:

    Factory()
    {
        m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;
        m_map["DerivedB"] = &AbstractFactory::createInstance<DerivedB>; 
    }

};

int main()
{
    Factory factory;

    Base *a = factory.Create("DerivedA");

    DerivedA *aa = dynamic_cast<DerivedA*>(a);

    std::cout << typeid(a).name()  << std::endl;
    std::cout << typeid(*a).name()  << std::endl;
    std::cout << typeid(aa).name() << std::endl;

    std::cout << aa->SayHello() << std::endl;

    std::cin.get();

    return 0;
}

アップデート

VS 2008 を使用して発生する正確なエラーは (ドイツ語で、申し訳ありませんが私の選択ではありませんでした..)

1>------ Erstellen gestartet: Projekt: ReflectiveFactory, Konfiguration: Debug Win32 ------
1>Kompilieren...
1>main.cpp
1>.\main.cpp(82) : error C2248: "AbstractFactory<BASE_T>::createInstance": Kein Zugriff auf protected Member, dessen Deklaration in der AbstractFactory<BASE_T>-Klasse erfolgte.
1>        with
1>        [
1>            BASE_T=Base
1>        ]
1>        .\main.cpp(55): Siehe Deklaration von 'AbstractFactory<BASE_T>::createInstance'
1>        with
1>        [
1>            BASE_T=Base
1>        ]
1>.\main.cpp(83) : error C2248: "AbstractFactory<BASE_T>::createInstance": Kein Zugriff auf protected Member, dessen Deklaration in der AbstractFactory<BASE_T>-Klasse erfolgte.
1>        with
1>        [
1>            BASE_T=Base
1>        ]
1>        .\main.cpp(55): Siehe Deklaration von 'AbstractFactory<BASE_T>::createInstance'
1>        with
1>        [
1>            BASE_T=Base
1>        ]
4

1 に答える 1

4

メソッドは、エンド ユーザーがアクセスできる必要があります。Createつまり、そのメソッドが存在するか、publicそこAbstractFactoryに移動さFactoryれて公開されます。の残りのコードはAbstractFactory保護できます。

gcc のみがコードを受け入れるようで (clang++、comau はそれを拒否します)、正当に拒否される可能性があります (標準を調べる必要があります)。とにかく簡単な回避策は、メンバー関数ポインターを提供するヘルパー関数を作成することです。

template <typename B>
template <typename D>
AbstractFactory<B>::func_ptr_type AbstractFactory<B>::getCreateInstancePtr() const {
   return &AbstractFactory<B>::createInstance<D>;
}

テンプレートのこのテンプレート メンバー メソッドはAbstractFactory、実際の独自のベースで直接呼び出しているため、保護できますFactory

Factory::Factory() {
        m_map["DerivedA"] = getCreateInstancePtr<DerivedA>();
        m_map["DerivedB"] = getCreateInstancePtr<DerivedB>();
}

標準で確認した後:

11.5p1 [class.protected]

メンバーへのポインターを形成する場合 (5.3.1) を除き、派生クラス自体 (またはそのクラスから派生した任意のクラス) へのポインター、参照、またはオブジェクトを介してアクセスする必要があります (5.2.5)。アクセスがメンバーへのポインターを形成することである場合、nested-name-specifier は派生クラス (またはそのクラスから派生した任意のクラス) に名前を付けるものとします

つまり、式m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;は正しくありませんが、m_map["DerivedA"] = &Factory::createInstance<DerivedA>;は正しいです (型ではなくアクセス指定子に関しては、左側が anB* (AbstractFactory<B>::*)( std::string )で右側が aB* (Factory::*)( std::string )であるため、代入は失敗します。

これは、他のあらゆる場所のセマンティクスとうまく機能しprotected、特に、独自の基本サブオブジェクト以外の保護されたメンバーにアクセスできない場合に役立ちます。

class base {
protected:
   int x;
};
struct derived : base {
   static void f( base& b ) {
      b.x = 5;                 // Error: cannot access base::x from this context
   }
};
于 2012-04-17T14:56:01.897 に答える