std::unique_ptr reset() 操作顺序 [英] std::unique_ptr reset() order of operations

查看:45
本文介绍了std::unique_ptr reset() 操作顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

调用void reset(pointer ptr = pointer()) noexcept; 调用以下操作

给定由 *this 管理的指针 current_ptr,按顺序执行以下操作:

Given current_ptr, the pointer that was managed by *this, performs the following actions, in this order:

  1. 保存当前指针的副本 old_ptr = current_ptr
  2. 用参数 current_ptr = ptr 覆盖当前指针
  3. 如果旧指针不为空,则删除之前管理的对象 if(old_ptr) get_deleter()(old_ptr).

cppreference

这个特定订单的原因是什么?为什么不只做 3) 然后做 2)?在这个问题中 std::unique_ptr::reset 检查托管指针无效?第一个答案引用了标准

What is the reason for this particular order? Why not just do 3) and then 2)? In this question std::unique_ptr::reset checks for managed pointer nullity? the first answer quotes the Standard

[…] [ 注意:这些操作的顺序很重要,因为对 get_deleter() 的调用可能会破坏 *this.——结尾说明]

[…] [ Note: The order of these operations is significant because the call to get_deleter() may destroy *this. —end note ]

这是唯一的原因吗?get_deleter() 如何破坏 unique_ptr (*this)?

Is this the only reason? How could get_deleter() destroy the unique_ptr (*this)?

推荐答案

在分析规定的步骤顺序时,考虑哪些步骤可以抛出,以及哪些状态会留下所有内容,这通常很有用 - 目的是我们应该永远不会陷入无法挽回的境地.

It's often useful when analysing a prescribed order of steps, to consider which ones could throw and and what state that would leave everything in - with the intent that we should never end up in an unrecoverable situation.

请注意,此处中的文档:

std::shared_ptr 不同,std::unique_ptr 可以通过任何满足NullablePointer 的自定义句柄类型来管理对象.例如,这允许通过提供定义 typedef boost::offset_ptr 指针Deleter 来管理位于共享内存中的对象;或其他花哨的指针.

Unlike std::shared_ptr, std::unique_ptr may manage an object through any custom handle type that satisfies NullablePointer. This allows, for example, managing objects located in shared memory, by supplying a Deleter that defines typedef boost::offset_ptr pointer; or another fancy pointer.

所以,按当前顺序:

  1. 保存当前指针的副本 old_ptr = current_ptr

  1. Saves a copy of the current pointer old_ptr = current_ptr

如果花式指针的拷贝构造函数抛出异常,unique_ptr 仍然拥有原始对象,而新对象是未拥有的:OK

if the copy constructor of fancy pointer throws, unique_ptr still owns the original object and new object is un-owned: OK

用参数 current_ptr = ptr 覆盖当前指针

Overwrites the current pointer with the argument current_ptr = ptr

如果花式指针的复制赋值抛出,unique_ptr 仍然拥有原始对象,而新对象是未拥有的:OK

if the copy assignment of fancy pointer throws, unique_ptr still owns the original object and new object is un-owned: OK

(这假设花式指针的复制赋值运算符满足通常的异常安全保证,但没有它就没有多少 unique_ptr 可以做的)

(this assumes the fancy pointer's copy assignment operator meets the usual exception-safety guarantee, but there's not much unique_ptr can do without that)

如果旧指针不为空,则删除之前管理的对象 if(old_ptr) get_deleter()(old_ptr)

If the old pointer was non-empty, deletes the previously managed object if(old_ptr) get_deleter()(old_ptr)

在这个阶段,unique_ptr 拥有新对象,删除旧对象是安全的.

at this stage unique_ptr owns the new object and it is safe to delete the old one.

换句话说,两种可能的结果是:

In other words, the two possible outcomes are:

std::unique_ptr<T, FancyDeleter> p = original_value();
try {
  auto tmp = new_contents();
  p.reset(tmp);
  // success
}
catch (...) {
  // p is unchanged, and I'm responsible for cleaning up tmp
}

按照您提议的顺序:

  1. 如果原指针非空,则删除

  1. If the original pointer is non-empty, delete it

在这个阶段unique_ptr是无效的:它已经提交了不可逆的改变(删除),如果下一步失败就没有办法恢复良好的状态

at this stage unique_ptr is invalid: it has committed the irreversible change (deletion) and there is no way to recover a good state if the next step fails

用参数 current_ptr = ptr 覆盖当前指针

Overwrite the current pointer with the argument current_ptr = ptr

如果花式指针的复制赋值抛出,我们的 unique_ptr 将无法使用:存储的指针是不确定的,我们无法恢复旧的

if the copy assignment of fancy pointer throws, our unique_ptr is rendered unusable: the stored pointer is indeterminate and we can't recover the old one

换句话说,我所说的不可恢复情况如下所示:

In other words, the unrecoverable situation I'm talking about is shown here:

std::unique_ptr<T, FancyDeleter> p = original_value();
try {
  auto tmp = new_contents();
  p.reset(tmp);
  // success
}
catch (...) {
  // I can clean up tmp, but can't do anything to fix p
}

在那次异常之后,p 甚至不能被安全地销毁,因为在其内部指针上调用删除器的结果可能是一个双重释放.

After that exception, p can't even be safely destroyed, because the result of calling the deleter on its internal pointer might be a double-free.

注意.删除器本身是不允许抛出的,所以我们不必担心.

NB. The deleter itself is not permitted to throw, so we don't have to worry about that.

注意事项

...对 get_deleter() 的调用可能会破坏 *this.

... the call to get_­deleter() might destroy *this.

听起来不对,但是调用get_deleter()(old_p)真的可能 ... if *old_p> 是一个包含自己的 unique_ptr 的对象.在这种情况下,删除器调用必须放在最后,因为在它之后,您几乎无法安全地对 unique_ptr 实例执行任何操作.

sounds wrong, but the call get_­deleter()(old_­p) really might ... if *old_p is an object containing a unique_ptr to itself. The deleter call has to go last in that case, because there is literally nothing you can safely do to the unique_ptr instance after it.

尽管这种极端情况是将删除器调用放在最后的充分理由,但我觉得强异常安全论点可能不那么人为(尽管具有指向自身的唯一指针的对象是否比具有独特指针的对象更常见或更不常见)投掷任务是任何人的猜测).

Although this corner case is a solid reason for putting the deleter call at the end, I feel like the strong exception safety argument is perhaps less contrived (although whether objects with unique pointers to themselves are more or less common than fancy pointers with throwing assignment is anyone's guess).

这篇关于std::unique_ptr reset() 操作顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆