【问题标题】:Idiom for exception safety relating to constructor parameters与构造函数参数相关的异常安全成语
【发布时间】:2012-10-10 00:19:36
【问题描述】:

我一直在查看我正在处理的一些代码,我们有类似的代码:

AutoPtr<A> x;
...
/// x gets initialized
...
B* y = new B(x.Detach());

其中 AutoPtr 是我们的 auto_ptr 版本,而 Detach() 返回拥有的指针并重置自身。此外,B() 获得 x 的所有权。

现在,我意识到如果 new 抛出 std::bad_alloc 会泄漏 x,所以我将代码更改为:

AutoPtr<A> x;
...
/// x gets initialized
...
B* y = new B(x.Get());
x.Detach();

但后来我意识到,如果 B() '拥有'指针,并且在其构造过程中发生异常,它应该负责删除参数本身(或者应该这样做?),所以 x 将被删除两次, B() 一次,x 的析构函数一次。

现在,是否有解决此问题的 C++ 习惯用法,例如,编写调用负责清理参数的构造函数的代码?我见过的大多数代码似乎都没有这样做......

【问题讨论】:

  • 我假设 B* 实际上是 AutoPtr&lt;B&gt;?
  • 哈哈,不,它不是,因为它被传递给另一个函数,该函数在下一行获得它的所有权。

标签: c++ exception-safety


【解决方案1】:

显而易见的解决方案似乎是将一个临时的AutoPtr&lt;A&gt; 传递给B 的构造函数:

AutoPtr<B> y(new B(AutoPtr<A>(x));

(这也为从new B() 返回的B* 添加了资源控制)。

B 的构造函数只需调用x.Detach() 来初始化它需要使用A* 初始化的任何内容。如果任何时候发生异常,AutoPtr&lt;A&gt; 将释放该对象。

如果你想保留x管理的A对象以防出现异常,可以将AutoPtr&lt;A&gt;&amp;传递给B的构造函数。

【讨论】:

  • 我不明白传递AutoPtr&lt;A&gt; 的副本而不是通过引用传递的原因。
  • 是的,这是有道理的。不幸的是,这段代码的构造函数到处都接受原始的拥有指针,所以我怀疑这是否会被实现,因为 bad_alloc 的可能性并不值得付出努力。
  • @Bwmat:至少添加这个构造函数,并开始使用它。有时安全好过从不安全。
  • @MooingDuck:最好的选择是添加两个构造函数:一个通过引用获取AutoPtr&lt;A&gt;,另一个通过值获取。对于最小的更改,即仅添加一个构造函数,我会选择按值获取 AutoPtr&lt;A&gt; 的构造函数,因为这样可以避免在只有临时变量时(例如,当指针从功能。由于复制 std::auto_ptr&lt;T&gt; 和可能的 AutoPtr&lt;T&gt; 很便宜,因此归结为对象是否通常命名,哪个更易于使用。
【解决方案2】:

...它应该负责删除参数本身(或者应该这样做?)

不,不应该。

B 在构造函数完成之前不存在,如果它不存在,它不应该声称拥有任何东西(在某种程度上;如果构造函数本身做了什么,那也需要是安全的) .

C++ 习惯用法是不使用原始指针作为所有权(包括y)! B 应该接受 AutoPtr 作为参数,以便调用者可以通过这种方式放弃所有权。这是std::unique_ptrstd::move的目标:

std::unique_ptr<A> x;

std::unique_ptr<B> y(new B(std::move(x)));

还要注意,真的new 也不应该这样使用;而是使用make_* 实用程序:

auto y = std::make_unique<B>(std::move(x));

但目前由于疏忽而缺少这一点。

【讨论】:

  • 我真的希望我们可以使用 std::unique_ptr,但是 C+11 暂时没有发生在这里 虽然
  • @Bwmat: auto_ptr 用于此目的。你的AutoPtr 也应该这样做。
  • 实际上,我在考虑移动语义。不过我想它们并不是真正需要的。
【解决方案3】:

类似这样的东西,可能是:

B* y = new B();
y->Attach(x.Detach());

B* y = new B();
(*y) = x;

【讨论】:

  • 两阶段初始化不好
  • @MooingDuck 动态分配AutoPtr 不好,其实
  • 如果我错了,请随时纠正我,但我没有看到任何人,包括 OP,建议或暗示动态分配 AutoPtr 会很好。跨度>
猜你喜欢
  • 2015-05-08
  • 2013-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
  • 2012-04-14
相关资源
最近更新 更多