动态创建一个继承的类,使用std :: map使用基类指针进行访问?

时间:2017-04-27 13:35:21

标签: c++ c++11 inheritance stl stdmap

不确定我能做到这一点。我有疑问是[这个其他问题]的变体(Can you create a std::map of inherited classes? )。

基本上,下面问题的答案是创建一个指向继承类的指针映射,其中映射可以根据第一个映射条目进行搜索。然后你调用一个指针来解决你想要的任何虚函数。

我已经使用一些命令实现了一个命令处理器应用程序。地图是:

std::map<string, BaseCmdClass*> cmd_map;

到目前为止一切顺利。但是,我们正在更新代码以提供可能数百个派生类。我不想在代码开头的工厂类中创建它们,使用太多内存。

我想在需要时动态构造一个继承的类,然后使用基类指针进行删除。我想避免一个大的case语句来调用每个构造函数。

有没有办法可以创建std::map或使用另一个可以查找要调用的构造函数的STL容器?然后,当我需要派生的构造函数时,我可以查找此映射以找到要调用的正确派生构造函数。

3 个答案:

答案 0 :(得分:0)

struct BaseCmdClass {
  virtual bool do_something() = 0;
  virtual ~BaseCmdClass() {}
};
using upCmd = std::unique_ptr<BaseCmdClass>;
using CmdFactory = std::function<upCmd()>;

using Command = std::string;

std::unordered_map<Command, CmdFactory>& command_factories() {
  static std::unordered_map<Command, CmdFactory> retval;
  return retval;
}
upCmd make_cmd( Command const& cmd ) {
  auto mit = command_factories().find(cmd);
  if (mit == command_factories().end() || !*mit) return {};
  return (*mit)();
}


// in CopyCmd.cpp
struct CopyCmd : BaseCmdClass
  bool do_something() override final {
    std::cout << "did copy";
  };
  static auto registered = command_factories().insert(
    "copy",
    []{ return std::make_unique<CopyCmd>(); }
  );
};

现在任何人都可以make_cmd("copy")通过指向其CopyCmd的唯一指针获取BaseCmdClass的实例。

答案 1 :(得分:0)

  

有没有办法可以创建一个std :: map或者使用另一个可以查找要调用的构造函数的STL容器?

不确定。您不能直接存储指向构造函数的指针,但可以存储指向调用构造函数的工厂函数的指针。

using CmdFactory = BaseCmdClass*();
std::map<std::string, CmdFactory*> factory_map;

您可以使用帮助程序模板生成工厂:

template<class T>
BaseCmdClass* factory() {
    return new T;
}

您可以添加这样的工厂:

factory_map["Derived1"] = factory<Derived1>;
  

我想在需要时动态构造一个继承的类,然后使用基类指针进行删除。我想避免一个大的case语句来调用每个构造函数。

只要析构函数是虚拟的,删除使用基类指针就可以了。不需要案例陈述。

进一步发展:

如果您需要有状态工厂,可以使用std::function而不是函数指针。

我建议从工厂返回std::unique_ptr<BaseCmdClass>以避免内存泄漏。

对于较低的内存开销,如果使用整数作为键,则可以使用数组而不是映射,但构造数组的代码必须知道所有子代。

答案 2 :(得分:0)

其中一个解决方案是为创作者提供并行层次结构:

class BaseCmdClass {
  ...
};

class BaseCmdCreator;
class BaseCmdFactory {
public:
    static BaseCmdFactory &instance();
    void registerCreator( const std::string &name, BaseCmdCreator *cr );
    ...
}; 

class BaseCmdCreator {
public:
    BaseCmdCreator( const std::string &name )
    {
         BaseCmdFactory::instance().registerCreator( name, this );
    }
    virtual std::unique_ptr<BaseCmdClass> create() = 0;
};

template<class T>
class CmdCreator : public BaseCmdCreator {
public:
    CmdCreator( const std::string &name ) : BaseCmdCreator( name ) {}
    virtual std::unique_ptr<BaseCmdClass> create() override
    {
        return std::make_unique<T>();
    }
};

现在如何使用它 - 在你定义特定CmdClass的.cpp文件中创建一个静态对象:

// Class FooCmdClass defined here

namespace {
    CmdCreator<FooCmdClass> registerFoo( "FooCmd" );
}

现在,如果您链​​接从此.cpp创建的目标文件,您的class FooCmdClass将被注册。您甚至可以通过插件机制完全动态地完成此操作。因此,添加新命令不需要对基类进行任何更改,只需将新的.cpp文件添加到项目中。