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

4 c++ boost

从我的上一个问题开始,我有一个抽象基类Action,它充当执行各种不同操作的接口.为了实现一个抽象层,我有一个ActionHandler类,它在这里存储各种动作:

class ActionHandler
{
public:
    ActionHandler();
    ~ActionHandler();
    Action& getAction(std::string ActionString);
private:
    boost::ptr_map<std::string, vx::modero::Action> cmdmap;
};
Run Code Online (Sandbox Code Playgroud)

我从上一个问题的回答中了解到,boost会自动处理将任何插入的指针类型(类)释放到此映射中.

所以,我现在尝试插入派生的东西Action,这发生在ActionHandler(ActionHandler :: ActionHandler)的构造函数中:

ActionHandler::ActionHandler()
{
    this->cmdmap.insert("help", new DisplayHelpAction());
};
Run Code Online (Sandbox Code Playgroud)

DisplayHelpAction公开子类Action.这样做会导致此错误:

error: no matching function for call to ‘boost::ptr_map<std::basic_string<char>, 
Action>::insert(const char [5], DisplayHelpAction*)’
Run Code Online (Sandbox Code Playgroud)

现在,从这里我使用的方法是:

std::pair<iterator,bool>  insert( key_type& k, T* x );
Run Code Online (Sandbox Code Playgroud)

所以我可以看到,使用多态在这里应该工作.我不想使用,boost::any因为我不希望此列表包含任何类型的指针.它应该符合Action或不存在指定的接口.

那么,我做错了什么?

我可以依靠简单地使用std::map并拥有我的析构函数delete,所以如果这不能合理地实现它不是一个显示塞子.我个人认为shared_ptrstd::map可能会更好,但这个已经尝试,我现在有这么-为什么- doesn't-这个工作综合症.

Mat*_* M. 9

@ Cubbi的答案是正确的,但没有解释为什么这样做.

传统上,const&除非它们是内置的,否则这些参数都是采用的,因此人们自然会期望:

insert(key_type const&, value*)
Run Code Online (Sandbox Code Playgroud)

这自然会允许:

someMap.insert("abc", new DerivedAction());
Run Code Online (Sandbox Code Playgroud)

但作者选择了签名:

insert(key_type&, value*)
Run Code Online (Sandbox Code Playgroud)

是的,这是故意的.

问题是带有原始指针的表单应该与内联 一起使用new,如您在自己的示例中所示,但是存在异常安全问题.

你可以在本周的大师看到Sutter对它的看法.

在C++中调用函数时,应在调用开始之前评估其所有参数,并且未指定参数的评估顺序.因此,如果参数的评估执行内存分配另一个操作(可能抛出),则存在风险.在你的例子中:

insert("abc", new DerivedAction());

// equivalent to

insert(std::string("abc"), new DerivedAction());
Run Code Online (Sandbox Code Playgroud)

在执行调用之前,有两个操作要做(以未指定的顺序):

  • 转换"abc"std::string
  • 在一个DerivedAction物体的免费商店建设

如果转换"abc"std::string抛出,并且在内存分配后进行了调度,则内存被泄露,因为您无法释放它.

通过强制第一个参数不是临时的,他们防止了这个错误.这是不够的(通常),因为任何函数调用都可以执行并仍然抛出,但它确实让你思考,不是吗:)?

注意:auto_ptr采用引用的版本反之亦然,它们会强制您事先分配内存,因此"abc"可能抛出的转换不再是风险,因为RAII将确保正确清理


Cub*_*bbi 7

正如您所指出的,您正在调用的方法是

std::pair<iterator,bool>  insert( key_type& k, T* x );
Run Code Online (Sandbox Code Playgroud)

但第一个参数不是可以绑定到非const引用的类型std::string.您必须将密钥设为左值

std::string key = "help";
cmdmap.insert(key, new DisplayHelpAction());
Run Code Online (Sandbox Code Playgroud)

或者使用带有const引用的表单(为了异常安全,仍然必须是两行)

std::auto_ptr<DisplayHelpAction> ptr(new DisplayHelpAction());
cmdmap.insert("help", ptr);
Run Code Online (Sandbox Code Playgroud)

  • 这解释了为什么密钥是通过非const引用传递的:http://www.boost.org/doc/libs/1_45_0/libs/ptr_container/doc/faq.html#why-does-ptr-map-t-insert -replace通吃两参数最关键和最指针代替-的酮-STD-对 - 和 - 为什么 - 是最关键通过逐非const参考 (4认同)