带有抽象基类的C ++ boost :: ptr_map导致插入问题

时间:2011-02-15 14:12:42

标签: c++ boost

从我的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_ptrstd::map可能会更好,但经过实验,我现在有了这样一个 - 为什么不是这个工作综合症。

3 个答案:

答案 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抛出异常,则分配给地图的对象可能会泄漏。