C ++中的反射工厂派生类无法访问受保护的方法?

时间:2012-04-17 14:33:54

标签: c++ templates inheritance reflection

answer 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..
}

到目前为止我已经完成了这项工作。但是,我对下面的代码有两个主要问题。它是丑陋的,如果我使抽象工厂的方法受到保护,它就会抱怨它无法访问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>        ]

1 个答案:

答案 0 :(得分:4)

最终用户必须可以访问Create方法,这意味着该方法在public中为AbstractFactory,或者已移至Factory并在那里公开。 <{1}}中的其余代码可以受到保护。

似乎只有gcc接受代码(clang ++,comeau拒绝它)并且它可以被理所当然地拒绝(我将不得不考虑标准)。无论如何,一个简单的解决方法是创建一个辅助函数,为您提供成员函数指针:

AbstractFactory

template <typename B> template <typename D> AbstractFactory<B>::func_ptr_type AbstractFactory<B>::getCreateInstancePtr() const { return &AbstractFactory<B>::createInstance<D>; } 模板的此模板成员方法可以受到保护,因为您在实际AbstractFactory中直接在自己的基础上调用它:

Factory

用标准检查后:

11.5p1 [class.protected]

  

除了形成指向成员(5.3.1)的指针外,访问必须通过a   指向派生类本身(或从该类派生的任何类)的指针,引用或对象(5.2.5)。 如果访问要形成指向成员的指针,则nested-name-specifier应命名派生类(或从该类派生的任何类)

即表达式Factory::Factory() { m_map["DerivedA"] = getCreateInstancePtr<DerivedA>(); m_map["DerivedB"] = getCreateInstancePtr<DerivedB>(); } 不正确,而m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;是正确的(关于访问说明符,但不是类型,左侧是m_map["DerivedA"] = &Factory::createInstance<DerivedA>;,右侧是B* (AbstractFactory<B>::*)( std::string ),因此作业失败。

这与其他地方B* (Factory::*)( std::string )的语义相得益彰,特别是无法访问除您自己的基础子对象之外的受保护成员:

protected