首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >有没有办法从包含类名称的字符串中实例化对象?

有没有办法从包含类名称的字符串中实例化对象?
EN

Stack Overflow用户
提问于 2009-02-24 16:00:00
回答 8查看 90.8K关注 0票数 152

我有一个文件: Base.h

代码语言:javascript
复制
class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/

和另一个文件: BaseFactory.h

代码语言:javascript
复制
#include "Base.h"

class BaseFactory
{
public:
  BaseFactory(const string &sClassName){msClassName = sClassName;};

  Base * Create()
  {
    if(msClassName == "DerivedA")
    {
      return new DerivedA();
    }
    else if(msClassName == "DerivedB")
    {
      return new DerivedB();
    }
    else if(/*etc...*/)
    {
      /*etc...*/
    }
  };
private:
  string msClassName;
};

/*etc.*/

有没有办法将这个字符串转换成实际的类型(类),这样BaseFactory就不必知道所有可能的派生类,并为每个派生类都使用if()?我能从这个字符串中产生一个类吗?

我认为这可以通过反射在C#中完成。在C++中有类似的东西吗?

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2009-02-24 16:30:29

不,没有,除非你自己做映射。C++没有创建其类型在运行时确定的对象的机制。不过,您可以使用map自己完成该映射:

代码语言:javascript
复制
template<typename T> Base * createInstance() { return new T; }

typedef std::map<std::string, Base*(*)()> map_type;

map_type map;
map["DerivedA"] = &createInstance<DerivedA>;
map["DerivedB"] = &createInstance<DerivedB>;

然后你就可以

代码语言:javascript
复制
return map[some_string]();

正在获取新实例。另一个想法是让类型自己注册:

代码语言:javascript
复制
// in base.hpp:
template<typename T> Base * createT() { return new T; }

struct BaseFactory {
    typedef std::map<std::string, Base*(*)()> map_type;

    static Base * createInstance(std::string const& s) {
        map_type::iterator it = getMap()->find(s);
        if(it == getMap()->end())
            return 0;
        return it->second();
    }

protected:
    static map_type * getMap() {
        // never delete'ed. (exist until program termination)
        // because we can't guarantee correct destruction order 
        if(!map) { map = new map_type; } 
        return map; 
    }

private:
    static map_type * map;
};

template<typename T>
struct DerivedRegister : BaseFactory { 
    DerivedRegister(std::string const& s) { 
        getMap()->insert(std::make_pair(s, &createT<T>));
    }
};

// in derivedb.hpp
class DerivedB {
    ...;
private:
    static DerivedRegister<DerivedB> reg;
};

// in derivedb.cpp:
DerivedRegister<DerivedB> DerivedB::reg("DerivedB");

您可以决定为注册创建一个宏

代码语言:javascript
复制
#define REGISTER_DEC_TYPE(NAME) \
    static DerivedRegister<NAME> reg

#define REGISTER_DEF_TYPE(NAME) \
    DerivedRegister<NAME> NAME::reg(#NAME)

不过,我相信这两个人还有更好的名字。这里使用的另一个可能有意义的东西是shared_ptr

如果你有一组没有公共基类的无关类型,你可以给函数指针一个返回类型boost::variant<A, B, C, D, ...>。例如,如果你有一个类Foo,Bar和Baz,它看起来像这样:

代码语言:javascript
复制
typedef boost::variant<Foo, Bar, Baz> variant_type;
template<typename T> variant_type createInstance() { 
    return variant_type(T()); 
}

typedef std::map<std::string, variant_type (*)()> map_type;

boost::variant就像一个联盟。通过查看用于初始化或赋值的对象,它知道存储在其中的是哪种类型。请看一下它的文档here。最后,原始函数指针的使用也有点陈旧。现代C++代码应该与特定的函数/类型解耦。您可能希望研究一下Boost.Function以寻找更好的方法。它看起来就像这样(地图):

代码语言:javascript
复制
typedef std::map<std::string, boost::function<variant_type()> > map_type;

包括std::shared_ptr在内的下一个版本的C++也将支持std::function

票数 240
EN

Stack Overflow用户

发布于 2009-02-24 16:08:49

没有。我对这个问题的首选解决方案是创建一个字典,它将名称映射到创建方法。想要像这样创建的类,然后用字典注册一个创建方法。在GoF patterns book中详细讨论了这一点。

票数 6
EN

Stack Overflow用户

发布于 2013-08-15 21:25:00

functional有一个非常灵活的工厂模板:http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html

不过,我更喜欢生成隐藏映射和对象创建机制的包装类。我遇到的常见场景是需要将一些基类的不同派生类映射到键,其中派生类都有一个可用的公共构造函数签名。这是我到目前为止想出的解决方案。

代码语言:javascript
复制
#ifndef GENERIC_FACTORY_HPP_INCLUDED

//BOOST_PP_IS_ITERATING is defined when we are iterating over this header file.
#ifndef BOOST_PP_IS_ITERATING

    //Included headers.
    #include <unordered_map>
    #include <functional>
    #include <boost/preprocessor/iteration/iterate.hpp>
    #include <boost/preprocessor/repetition.hpp>

    //The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classes which will be generated.
    #ifndef GENERIC_FACTORY_MAX_ARITY
        #define GENERIC_FACTORY_MAX_ARITY 10
    #endif

    //This macro magic generates GENERIC_FACTORY_MAX_ARITY + 1 versions of the GenericFactory class.
    //Each class generated will have a suffix of the number of parameters taken by the derived type constructors.
    #define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
    #define BOOST_PP_ITERATION_LIMITS (0,GENERIC_FACTORY_MAX_ARITY)
    #include BOOST_PP_ITERATE()

    #define GENERIC_FACTORY_HPP_INCLUDED

#else

    #define N BOOST_PP_ITERATION() //This is the Nth iteration of the header file.
    #define GENERIC_FACTORY_APPEND_PLACEHOLDER(z, current, last) BOOST_PP_COMMA() BOOST_PP_CAT(std::placeholders::_, BOOST_PP_ADD(current, 1))

    //This is the class which we are generating multiple times
    template <class KeyType, class BasePointerType BOOST_PP_ENUM_TRAILING_PARAMS(N, typename T)>
    class BOOST_PP_CAT(GenericFactory_, N)
    {
        public:
            typedef BasePointerType result_type;

        public:
            virtual ~BOOST_PP_CAT(GenericFactory_, N)() {}

            //Registers a derived type against a particular key.
            template <class DerivedType>
            void Register(const KeyType& key)
            {
                m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl<DerivedType>, this BOOST_PP_REPEAT(N, GENERIC_FACTORY_APPEND_PLACEHOLDER, N));
            }

            //Deregisters an existing registration.
            bool Deregister(const KeyType& key)
            {
                return (m_creatorMap.erase(key) == 1);
            }

            //Returns true if the key is registered in this factory, false otherwise.
            bool IsCreatable(const KeyType& key) const
            {
                return (m_creatorMap.count(key) != 0);
            }

            //Creates the derived type associated with key. Throws std::out_of_range if key not found.
            BasePointerType Create(const KeyType& key BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(N,const T,& a)) const
            {
                return m_creatorMap.at(key)(BOOST_PP_ENUM_PARAMS(N,a));
            }

        private:
            //This method performs the creation of the derived type object on the heap.
            template <class DerivedType>
            BasePointerType CreateImpl(BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& a))
            {
                BasePointerType pNewObject(new DerivedType(BOOST_PP_ENUM_PARAMS(N,a)));
                return pNewObject;
            }

        private:
            typedef std::function<BasePointerType (BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& BOOST_PP_INTERCEPT))> CreatorFuncType;
            typedef std::unordered_map<KeyType, CreatorFuncType> CreatorMapType;
            CreatorMapType m_creatorMap;
    };

    #undef N
    #undef GENERIC_FACTORY_APPEND_PLACEHOLDER

#endif // defined(BOOST_PP_IS_ITERATING)
#endif // include guard

我通常反对大量使用宏,但在这里我有一个例外。上面的代码生成一个名为GenericFactory_N的类的GENERIC_FACTORY_MAX_ARITY +1个版本,对于0和GENERIC_FACTORY_MAX_ARITY之间的每个N,包括0和GENERIC_FACTORY_MAX_ARITY。

使用生成的类模板很容易。假设您希望工厂使用字符串映射创建BaseClass派生对象。每个派生对象都接受3个整数作为构造函数参数。

代码语言:javascript
复制
#include "GenericFactory.hpp"

typedef GenericFactory_3<std::string, std::shared_ptr<BaseClass>, int, int int> factory_type;

factory_type factory;
factory.Register<DerivedClass1>("DerivedType1");
factory.Register<DerivedClass2>("DerivedType2");
factory.Register<DerivedClass3>("DerivedType3");

factory_type::result_type someNewObject1 = factory.Create("DerivedType2", 1, 2, 3);
factory_type::result_type someNewObject2 = factory.Create("DerivedType1", 4, 5, 6);

GenericFactory_N类析构函数是虚拟的,以允许执行以下操作。

代码语言:javascript
复制
class SomeBaseFactory : public GenericFactory_2<int, BaseType*, std::string, bool>
{
    public:
        SomeBaseFactory() : GenericFactory_2()
        {
            Register<SomeDerived1>(1);
            Register<SomeDerived2>(2);
        }
}; 

SomeBaseFactory factory;
SomeBaseFactory::result_type someObject = factory.Create(1, "Hi", true);
delete someObject;

请注意,泛型工厂生成器宏的这一行

代码语言:javascript
复制
#define BOOST_PP_FILENAME_1 "GenericFactory.hpp"

假定通用工厂头文件名为GenericFactory.hpp

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/582331

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档