从我的last question开始,我有一个抽象基类Action
,它充当执行各种不同操作的接口。为了实现抽象层,我有一个ActionHandler类,它在此存储各种动作:
class ActionHandler
{
public:
ActionHandler();
~ActionHandler();
Action& getAction(std::string ActionString);
private:
boost::ptr_map<std::string, vx::modero::Action> cmdmap;
};
我从上一个问题的回答中了解到,boost会自动处理将任何插入的指针类型(类)释放到此映射中。
所以,我现在尝试插入从Action
派生的东西,这发生在ActionHandler的构造函数中(ActionHandler :: ActionHandler):
ActionHandler::ActionHandler()
{
this->cmdmap.insert("help", new DisplayHelpAction());
};
DisplayHelpAction
公开子类Action
。这样做会导致此错误:
error: no matching function for call to ‘boost::ptr_map<std::basic_string<char>,
Action>::insert(const char [5], DisplayHelpAction*)’
现在,从here我正在使用的方法是:
std::pair<iterator,bool> insert( key_type& k, T* x );
据我所知,使用多态在这里应该可行。我不想使用boost::any
,因为我不希望此列表包含任何类型的指针。它应该符合Action
指定的接口或不符合。
那么,我做错了什么?
我可以简单地使用std::map
并使用我的析构函数delete
,所以如果这不能合理地实现,那么它不是一个显示阻止者。我个人认为shared_ptr
与std::map
可能会更好,但经过实验,我现在有了这样一个 - 为什么不是这个工作综合症。
答案 0 :(得分:9)
@ Cubbi的回答是正确的,但没有解释为什么会这样做。
传统上,const&
采用的是参数,除非它们是内置的,所以人们自然会期望:
insert(key_type const&, value*)
这自然会允许:
someMap.insert("abc", new DerivedAction());
但作者选择了签名:
insert(key_type&, value*)
是的,故意。
问题是,带有原始指针的表单应该与内联 new
一起使用,如您在自己的示例中所示,但是存在异常安全问题。
你可以在Guru Of The Week阅读Sutter对它的看法。
在C ++中调用函数时,应在调用开始之前评估其所有参数,并且未指定参数的评估顺序。因此,如果参数的评估执行内存分配 AND 另一个操作(可能抛出),则存在风险。在您的示例中:
insert("abc", new DerivedAction());
// equivalent to
insert(std::string("abc"), new DerivedAction());
在执行调用之前,有两个操作要做(以未指定的顺序):
"abc"
转换为std::string
DerivedAction
对象如果将"abc"
转换为std::string
,并且在内存分配后进行了调度,那么内存就会被泄露,因为您无法释放它
通过强制第一个参数不是临时的,他们防止了这个错误。这是不够的(通常),因为任何函数调用都可以执行并仍然抛出,但它确实让你思考,不是吗:)?
注意:通过引用获取auto_ptr
的版本反之亦然,它们会强制您事先分配内存,因此"abc"
可能抛出的转换不是风险因为RAII将确保正确清理
答案 1 :(得分:7)
正如您所指出的,您正在调用的方法是
std::pair<iterator,bool> insert( key_type& k, T* x );
但第一个参数不是可以绑定到std::string
的非const引用的类型。您必须将密钥设为左值
std::string key = "help";
cmdmap.insert(key, new DisplayHelpAction());
或者使用带有const引用的表单(为了安全性,仍然必须是两行)
std::auto_ptr<DisplayHelpAction> ptr(new DisplayHelpAction());
cmdmap.insert("help", ptr);
答案 2 :(得分:1)
您需要使用auto_ptr
传递值:
std::auto_ptr<Action> action (new DisplayHelpAction ());
cmdmap.insert ("help", action);
...或在调用insert
之前创建密钥,以便它将作为非const引用传递,在这种情况下,您不需要自动指针:
std::string key ("help");
cmdmap.insert (key, new DisplayHelpAction());
在第二个示例中,通过在ptr_map_adapter
类中使键引用非常量来强制使用。否则,如果std::string
抛出异常,则分配给地图的对象可能会泄漏。