2012-04-17 7 views
1

によって誘発されたanswer Johannes Schaub's answer私は反射的な工場を実装しようとしました。アイデアは、クラスの名前を、ヒープ上に関連するオブジェクトを作成し、共通の基本クラスへのポインタを返すメソッドに渡すことによってオブジェクトを作成できるということです。 dynamic_castとRTTIを使用すると、オブジェクトは必要に応じて元の型にキャストすることができます。一つは、以下に示すように反射ファクトリを使用することができるはずです。Reflective Factory in C++派生クラスは保護されたメソッドにアクセスできませんか?

// 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 ::について知っていますが、これまで私はそれを使用していませんでした。多分私は将来するでしょう。

#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>  ] 
+0

エラーとは何ですか?あなたの答えは –

答えて

4

Create方法は最後までアクセス可能でなければならないですつまり、その方法がAbstractFactoryにあるpublicであるか、Factoryに移動されて公開されていることを意味します。 AbstractFactoryのコードの残りの部分は保護することができます。

gccだけがコードを受け入れているようです(clang ++、それは拒否します)、正当に拒否される可能性があります(私は標準を調べなければなりません)。簡単な回避策は、あなたのためのメンバ関数ポインタを提供しますヘルパー関数を作成することですいずれにせよ:あなたが直接あなたにそれを呼び出しているよう

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

AbstractFactoryテンプレートのこのテンプレートメンバメソッドは、保護することができます実際Factoryで自分のベース:

11.5p1 [CLAS:

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

標準でチェックした後、部材(5.3.1)へのポインタを形成する場合、アクセスは を参照へのポインタ、または派生クラス自体(又はそれに由来する任意のクラスのオブジェクトを介してでなければならない場合を除きs.protected]

クラス)(5.2.5)。 アクセスがメンバへのポインタを形成する場合、ネストされた名前指定子は派生クラス(またはそのクラスから派生したクラス)の名前をとする。ある

、表現m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;m_map["DerivedA"] = &Factory::createInstance<DerivedA>;は、アクセス指定について(正しいのですが、ないタイプながら、左手側はB* (AbstractFactory<B>::*)(std::string)で、右側がB* (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 
    } 
}; 
+0

Thxです。最初は私が正確にそれを持っていた、すべてのメソッドはCreate()publicを除いて保護されていました。しかし、私はVS 2008でエラーが発生しました:エラーC2248: "AbstractFactory :: createInstance"メンバーなどへのアクセスなし – Nils

+0

@Nils:コードはコンパイルする必要があります。これはコンパイラの問題かもしれません。ベースがテンプレートではない(つまり、AbstractFactory に相当するものを手動で作成する)別の同等のテストケースを作成して、コンパイラがまだ文句を言うかどうか確認してください。 –

+0

ええ、私はそう考えています。llvmとclangで家で試してみます – Nils

関連する問題