10

我正在阅读这个答案,作者提到了提升最佳实践,其中说:

避免使用未命名的 shared_ptr 临时对象来节省输入;要了解为什么这是危险的,请考虑以下示例:

void f(shared_ptr<int>, int); 
int g();

void ok() {
    shared_ptr<int> p(new int(2));
    f(p, g());
}

void bad() {
    f(shared_ptr<int>(new int(2)), g());
}

函数 ok 遵循信中的准则,而 bad 构造临时 shared_ptr 就地,承认内存泄漏的可能性。由于函数参数以未指定的顺序进行评估,因此可能首先评估 new int(2),然后评估 g(),如果 g 抛出异常,我们可能永远无法访问 shared_ptr 构造函数。<...>

上述异常安全问题也可以通过使用 boost/make_shared.hpp 中定义的 make_shared 或 allocate_shared 工厂函数来消除。这些工厂功能还通过合并分配提供了效率优势。

我想我会开始使用make_shared,但我想知道这条建议是否仍然适用于 C++11 shared_ptr。我问是因为我真的不完全理解为什么投掷g()会阻止 ctor 被调用。

4

2 回答 2

9

是的,C++11 的shared_ptr工作方式相同。

我之所以问是因为我并不完全理解为什么投掷 g() 会阻止 ctor 被调用。

你不明白什么?这是一个操作顺序问题,标准不需要特定的顺序。让我们将语句解压缩成一系列表达式:

auto __temp = new int(2);
auto &&__temp2 = g();
auto __temp3 = shared_ptr<int>(__temp);

你现在看到问题了吗?如果g抛出,则__temp3永远不会被初始化。因此,__temp会被泄露

C++ 标准不要求以这种方式解包语句。但它也不禁止它。编译器可以自由地对这些独立的表达式进行排序,但它认为合适。

于 2013-08-28T02:19:24.307 回答
4

我认为 C++11 中的评估顺序没有任何改变。也就是说,std::make_shared<T>(...)除了只需要一个内存分配而不是两个内存分配之外,就安全性而言,使用是更好的选择。

关于什么问题:以下f()论证的评估是完全有效的:

  1. $tmp = new int(1)$tmp用于表示编译器提供的临时变量)
  2. g()
  3. std::shared_ptr<int>($tmp)

现在,如果g()throws,则永远不会执行其他部分的评估,即std::shared_ptr<int>永远不会构造和$tmp泄漏。

于 2013-08-28T02:22:00.237 回答