我正在构建一个应用程序,其用法如下所示:
application --command --option1=? --option2=2?
基本上,可以有任意数量的选项,但每个应用程序实例只能有一个命令。类似于git的工作方式。
现在,我以为我会用C ++编写它来获得一些提升和体验,并且我会继续阅读一些我一直在阅读的设计模式。所以,我实现了这个:
class Action
{
public:
void AddParameter(std::string key, boost::any p);
virtual unsigned int ExecuteAction();
protected:
std::map<std::string, boost::any> parameters;
};
无论如何我会解释我的逻辑,只是为了检查它 - 这是一个抽象的行动。所有操作都需要添加选项,因此参数映射,以便我们可以在此级别实现,但我们希望ExecuteAction
由派生类实现,例如我的简单示例DisplayHelpAction
,它几乎可以实现它在锡上说的是什么。
所以现在我写了一个工厂,就像这样:
class DetermineAction
{
public:
DetermineAction();
vx::modero::Action getAction(std::string ActionString);
private:
std::map<std::string, vx::modero::Action> cmdmap;
};
逻辑是构造函数将创建一个可以请求的可能字符串的映射,getAction将执行它所说的 - 给它一个命令字符串,它将为您提供一个派生自Action
的类它实现了所需的功能。
我遇到了那个构造函数的问题。我正在尝试这个:
this->cmdmap = std::map<std::string, Action>();
this->cmdmap.insert(pair<string, Action>("help", DisplayHelpAction()));
this->cmdmap.insert(pair<string, Action>("license", DisplayLicenseAction()));
这导致了很多错误。现在,我习惯了Java的接口方式,所以你使用:
Interface I = new ConcreteClass();
和Java喜欢它。所以这就是我想在这里实现的那种想法,因为我想要实现的getAction
就是:
return this->cmdmap[ActionString];
哪个应该返回一个派生自Action的类,然后我就可以开始添加参数并调用execute。
总而言之,我有两个密切相关的问题:
如何设置我的类映射解决方案,以便我可以返回正确的类?具体投诉是链接时间,并且是:
Linking CXX executable myapp
CMakeFiles/myapp.dir/abstractcmd.cpp.o: In function `nf::Action::Action()':
abstractcmd.cpp:(.text._ZN2vx6modero6ActionC2Ev[_ZN2vx6modero6ActionC5Ev]+0x13): undefined reference to `vtable for nf::Action'
仅仅因为它可能是相关的,我正在使用boost::program_options
进行命令行解析。
修改1 :好的,我已根据Eugen的回答将Action
替换为Action*
,并尝试将new SomethingThatSubclassesAction
添加到地图中。我仍然得到vtable错误。
答案 0 :(得分:3)
需要立即说明的一点是,运行时多态性在C ++中通过指向基类的指针而不是值。因此,当std::map<std::string, Action>
复制到std::map<std::string, Action*>
时,您的DisplayHelpAction
必须为map
或您的衍生操作(即Action*
)为sliced。存储map
也意味着您在完成后需要明确地处理释放boost::ptr_map<std::string,Action>
值。 注意:您可以使用boost::ptr_map(std::map<std::string,boost::shared_ptr<Action> >
)(如@Fred Nurk指出的那样)或boost::shared_ptr(Action*
)来担心关于明确释放分配的Action* getAction(std::string ActionString);
关于'Action getAction(std :: string ActionString);'的相同事情它需要成为virtual unsigned int ExecuteAction();
。
由于未提供virtual unsigned int ExecuteAction() = 0;
的实现而导致链接器错误(最有可能)。另外我认为将其设为纯虚拟(Action
)是有意义的 - 在这种情况下,您不需要为它提供实现。它还将为Action
类的Java接口提供关闭语义。
除非你有充分的理由让boost:program_options
派生的对象不知道整个std::map<std::string, boost::any>
我会将其传递下来并让他们每个人直接访问它而不是构建{ {1}}。
我将DetermineAction
重命名为ActionManager
或ActionHandler
。