【问题标题】:Exception safety and make_unique异常安全和 make_unique
【发布时间】:2013-10-28 15:33:33
【问题描述】:

澄清一下,使用make_unique 仅在表达式中有多个分配时才增加异常安全性,而不仅仅是一个,对吗?例如

void f(T*);

f(new T);

是完全异常安全的(就分配和东西而言),而

void f(T*, T*);

f(new T, new T);

不是,对吗?

【问题讨论】:

  • 您的问题似乎自相矛盾。首先你断言多重分配是异常安全的,然后你展示了一个表面上与实际情况相反的例子。
  • @LightnessRacesinOrbit 不,我断言多次分配不是异常安全的。我说过“make_unique 仅在表达式中有多个分配时才增加异常安全性”,这意味着它只为一个分配添加任何内容。
  • 您的问题在您的示例中没有使用make_uniqueunique_ptr,因为?
  • @Yakk 因为它不需要它
  • multiple allocations in an expression 但通过new T 进行的每个分配 一个单独的表达式,因此您有相反的情况:每个多个表达式分配一个。函数调用是 itself 一个包含其他 2 的表达式并不会改变这一点。但除了措辞之外,你走在了正确的轨道上。根据我的阅读,Sutter 制定的规则是,通过从单独的函数调用返回,在其自己的 statementsequence 中执行每个分配,从而避免奇怪的排序& 泄漏:gotw.ca/gotw/056.htm (oldie-but-goodie)

标签: c++ c++11 unique-ptr exception-safety


【解决方案1】:

不仅当你有多个分配时,而且当你可以在不同的地方扔时。考虑一下:

f(make_unique<T>(), function_that_can_throw());

对比:

f(unique_ptr<T>(new T), function_that_can_throw());

第二种情况,允许编译器调用(按顺序):

  • new T
  • function_that_can_throw()
  • unique_ptr&lt;T&gt;(...)

很明显,如果function_that_can_throw 真的抛出了,那么你就会泄漏。 make_unique 阻止了这种情况。

当然,第二次分配(如您的问题)只是function_that_can_throw() 的一个特例。

作为一般经验法则,只需使用make_unique 以使您的代码保持一致。当您需要 unique_ptr 时,它始终是正确的(阅读:异常安全),并且它对性能没有任何影响,因此没有理由不使用它(实际上 不是使用它会带来很多问题)。

【讨论】:

  • 谢谢,这就是我要找的。很抱歉毁了你的 2^13 代表。
  • 有点晚了,不过make_unique可以根据cppreference自己抛出:make_unique"可能抛出std::bad_alloc或者T的构造函数抛出的任何异常。如果抛出异常,这个函数没有作用。”那么该异常如何安全?使用 make_unique 初始化但 make_unique 抛出的 unique_ptr 对象(命名或临时)会发生什么情况?
  • 异常安全不是(仅)关于“没有异常”,而是关于“在遇到异常时保证正确的行为”。 make_unique 文档很清楚:如果出现异常则无效,这是一个强有力的保证(参见 en.wikipedia.org/wiki/Exception_safety)。并且 unique_ptr 尚未构造,因此再次无效;异常只是简单地传播。
【解决方案2】:

从 C++17 开始,异常安全问题已通过改写 [expr.call] 得到修复

参数的初始化,包括每个相关的值计算和副作用,相对于任何其他参数的初始化是不确定的。

这里indeterminately sequenced表示一个在另一个之前被排序,但没有指定是哪个。

f(unique_ptr<T>(new T), function_that_can_throw());

只能有两种可能的执行顺序

  1. new Tunique_ptr&lt;T&gt;::unique_ptrfunction_that_can_throw
  2. function_that_can_thrownew Tunique_ptr&lt;T&gt;::unique_ptr

这意味着它现在是异常安全的。

【讨论】:

    【解决方案3】:

    我认为您最好使用 std::unique_ptr&lt;T&gt; 实际比较事物:

    void f(std::unique_ptr<T>);
    
    f(std::unique_ptr<T>(new T));
    f(std::make_unique<T>());
    

    如果抛出异常,这些调用都不会泄漏。不过

    void f(std::unique_ptr<T>, std::unique_ptr<T>);
    
    g(std::unique_ptr<T>(new T), std::unique_ptr<T>(new T));
    g(std::make_unique<T>(), std::make_unique<T>());
    

    在这种情况下,如果抛出异常,显式使用 std::unique_ptr&lt;T&gt; 的版本可能会泄漏(因为编译器可能会在构造任何一个临时变量之前开始评估 new 表达式)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-16
      • 1970-01-01
      • 2016-08-05
      • 1970-01-01
      • 2010-12-02
      • 2016-11-20
      • 2015-09-13
      相关资源
      最近更新 更多