【问题标题】:Why does boost not have a make_scoped()?为什么 boost 没有 make_scoped()?
【发布时间】:2012-07-05 02:46:45
【问题描述】:

Boost 的 make_shared() 函数承诺在尝试创建 shared_ptr 时是异常安全的。

为什么没有make_scoped() 等价物?是否有通用的最佳做法?

这是来自boost::scoped_ptr documentation 的代码示例,对我来说似乎不安全:

    boost::scoped_ptr<Shoe> x(new Shoe);

这行代码会依次完成这三件事

  • Shoe分配堆内存
  • 调用Shoe的构造函数
  • 调用boost::scoped_ptr&lt;Shoe&gt;的构造函数

如果Shoe 的构造函数抛出异常,内存将被泄露。 (参见 R. Martinho Fernandes 的回答)scoped_ptr 不会处理释放,因为它还没有被构造。

这是疏忽吗?或者有没有我没注意到的解决方案?

【问题讨论】:

  • 这个例子是安全的,但不是:f(boost::scoped_ptr&lt;Shoe&gt;(new Shoe), g());。解决问题的编码实践:始终将智能指针命名为变量或成员,不要将它们构造为临时子表达式。

标签: c++ boost make-shared scoped-ptr


【解决方案1】:

scoped_ptr 早于移动语义并且在设计上是不可复制的。因此,make_scoped 将无法实现,因为为了从函数返回对象,其类型必须是可移动的或可复制的。

【讨论】:

  • 要明确:从 C++11 开始,可以从函数返回不可复制、不可移动的对象:wandbox.org/permlink/GLIgM5nA9ROJahvk 但就像您指出的那样,scoped_ptr 早于 c++11 蚂蚁,即线索在这里。
【解决方案2】:

如果构造函数失败,则不会泄漏内存。这是new 语义的一部分,不涉及智能指针:

struct Foo { Foo() { throw 23; } };
new Foo(); // no memory leaked

make_shared 提供的额外异常安全性来自于当您在一个表达式中初始化两个 shared_ptrs 并且 这两个初始化没有顺序,就像函数调用参数中的情况一样:

struct Bar {
    Bar(bool fail) {
        if(fail) throw 17;
    }
}
f(shared_ptr<Bar>(new Bar(true)), shared_ptr<Bar>(new Bar(false)));

由于new Bar(true)shared_ptr&lt;Bar&gt;(new Bar(true))new Bar(false)shared_ptr&lt;Bar&gt;(new Bar(false)) 的评估之间没有顺序,可能会发生以下情况:

  1. new Bar(false) 被评估并成功:内存已分配;
  2. new Bar(true) 被评估并失败:它不会泄漏此评估导致的内存;

此时没有构造shared_ptr,因此#1 中分配的内存现在泄漏了。

【讨论】:

  • 这听起来像是我理解你的意思吗? new 承诺捕获构造函数抛出的异常,释放内存,然后重新抛出?
  • @Drew:是的,大意如此。如果您有兴趣,请参阅第 5.3.4 节第 18 段的标准。
  • 为了完整起见,make_shared(在大多数实现中)的另一个目的是它在创建对象的同时为引用计数分配内存(为对象和count) 以便在两个堆分配时不会导致性能损失。 scoped_ptr(或 c++11 中的 unique_ptr 不需要引用计数,因此在该部门不会从中获得任何收益。
【解决方案3】:

如果 Shoe 抛出,则 Shoe 没有被构造,因此 scoped_ptr 无法真正做任何事情。不? scoped_ptr x 在堆栈上,将在范围退出时被清理。

【讨论】:

    猜你喜欢
    • 2011-01-21
    • 2011-03-10
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-14
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多