首页 > 解决方案 > 使用基于字符串的类型创建多态 C++ 对象的最佳方法?

问题描述

我想根据从一些配置文本文件中读取的字符串创建具有多态类型的对象。一个简单的解决方案是为每个可能的类型分配一个字符串,然后将 else-if 链中的配置字符串与所有定义的类型进行比较。就像是:

class Base
{
   virtual std::string GetStringType() = 0;
};

class Derived1 : public Base
{
   std::string GetStringType() override { return "Derived1"; }
};

class Derived2 : public Base
{
   std::string GetStringType() override { return "Derived2"; }
};

// etc ...

void main(int argc, char *argv[])
{
   std::unique_ptr<Base> ptr;
   auto derived1 = std::make_unique<Derived1>();
   auto derived2 = std::make_unique<Derived2>();
   // etc ...

   std::string stringType(argv[1]);

   if (stringType == derived1->GetStringType())
       ptr = std::make_unique<decltype(derived1)>();
   else if (stringType == derived2->GetStringType())
       ptr = std::make_unique<decltype(derived2)>();
   // etc ...
}

但是,使用这种方法,每次添加新的派生类时,都需要手动添加新的 else-if 分支,我试图避免这种情况。有没有更好、更自动化的方法呢?

此外,在理想情况下,当在某处定义新的派生类(刚刚定义,未实例化)时,我也想自动检查它。这有可能吗?我会为任何有效的解决方案感到高兴,包括宏。

标签: c++typespolymorphism

解决方案


一个简单的基于地图的工厂可以做到这一点:

#include <map>
#include <functional>
#include <string>
#include <memory>

class Base 
{
public:
  virtual ~Base() = default;

  static std::unique_ptr<Base> create(const std::string& name) {
      return factories_.at(name)();
  }

  template<typename T>
  static void registerDerived() {
    static_assert(std::is_base_of_v<Base, T>);

    factories_[T::GetStringType()] = std::make_unique<T>;
  }

private:
  static std::map<std::string, std::function<std::unique_ptr<Base>()>> factories_;
};

std::map<std::string, std::function<std::unique_ptr<Base>()>> Base::factories_;

class Derived1 : public Base
{
public:
   static std::string GetStringType() { return "Derived1"; }
};

class Derived2 : public Base
{
public:
   static std::string GetStringType() { return "Derived2"; }
};



int main(int argc, char *argv[]) {
  Base::registerDerived<Derived1>();
  Base::registerDerived<Derived2>();
  // etc...

  std::unique_ptr<Base> ptr = Base::create(argv[1]);
  // ...
}

推荐阅读