有没有办法从保存类名的字符串中实例化对象?

新手上路,请多包涵

我有一个文件:Base.h

 class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/

和另一个文件:BaseFactory.h

 #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 ++中有类似的东西吗?

原文由 Gal Goldman 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 729
2 个回答

不,没有,除非您自己进行映射。 C++ 没有创建类型在运行时确定的对象的机制。不过,您可以使用地图自己进行映射:

 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>;

然后你可以做

return map[some_string]();

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

 // 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");

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

#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,它看起来像这样:

 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 就像一个联合体。它通过查看用于初始化或分配给它的对象来知道其中存储了哪种类型。在 这里 查看它的文档。最后,使用原始函数指针也有点陈旧。现代 C++ 代码应该与特定的函数/类型分离。您可能想查看 Boost.Function 以寻找更好的方法。它看起来像这样(地图):

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

std::function 也将在下一版本的 C++ 中可用,包括 std::shared_ptr

原文由 Johannes Schaub - litb 发布,翻译遵循 CC BY-SA 3.0 许可协议

有一个现成的反射库 https://www.rttr.org/ 。您可以使用它轻松地通过字符串实例化类。

 struct MyStruct { MyStruct() {}; void func(double) {}; int data; };

RTTR_REGISTRATION
{
    registration::class_<MyStruct>("MyStruct")
         .constructor<>()
         .property("data", &MyStruct::data)
         .method("func", &MyStruct::func);
}

type t = type::get_by_name("MyStruct");
variant var = t.create();

原文由 Valeri Tonchev 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏