调用void reset( pointer ptr = pointer() ) noexcept;调用以下操作
给定current_ptr,由*this管理的指针按照以下顺序执行以下操作:
这个特别订单的原因是什么?为什么不直接做然后再做呢?在这个问题中,ptr::重置检查托管指针无效?的第一个答案引用了标准
…
这是唯一的原因吗?get_deleter()如何摧毁unique_ptr (*this)?
发布于 2021-09-07 22:14:21
在分析规定的步骤顺序时,考虑哪些步骤可以抛出,以及什么状态会使一切都处于这样的状态,这通常是有用的--这样做的目的是我们永远不应该在无法恢复的情况下结束。
请注意,在docs 这里中:
与
std::shared_ptr不同,std::unique_ptr可以通过满足NullablePointer的任何自定义句柄类型来管理对象。例如,这允许通过提供定义Deleter的typedef boost::offset_ptr pointer或另一个高级指针来管理位于共享内存中的对象。
因此,按照目前的顺序:
换言之,这两种可能的结果是:
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
}按照你提议的顺序:
换句话说,我所说的无法恢复的情况如下所示:
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甚至无法被安全销毁,因为调用其内部指针上的删除器的结果可能是双空闲的。
注意:删除器本身是不允许抛出的,所以我们不必担心。
纸条上写着
..。对
get_deleter()的调用可能会破坏*this。
听起来不对,但get_deleter()(old_p)的电话真的可能.如果*old_p是一个对象,它本身包含一个unique_ptr。在这种情况下,删除器调用必须是最后一个调用,因为从字面上说,在此之后您无法安全地对unique_ptr实例执行任何操作。
虽然这个角落的情况是将删除器调用放在末尾的一个坚实的原因,但我觉得强大的异常安全论点可能没有那么人为的(尽管与抛出赋值的花式指针相比,具有唯一指针的对象或多或少是比较常见的)。
发布于 2021-09-07 19:34:47
与这类似,您可以创建使用std::unique_ptr管理自己生命周期的对象,而无需std::shared_ptr的开销,只要需要std::unique_ptr。在下面的例子中,我们创建了一个“火与忘”的任务,自毁后,它的工作。动机可以找到这里。
#include <cstdio>
#include <future>
#include <memory>
#include <thread>
using namespace std::chrono_literals;
class Task
{
public:
Task(Task&&) = delete;
Task& operator=(Task&&) = delete;
Task(const Task&) = delete;
Task& operator=(const Task&) = delete;
~Task() { std::printf("Destroyed.\n"); }
static Task* CreateTask()
{
// Can't use std::make_unique because the constructor is private.
std::unique_ptr<Task> task{new Task{}};
Task* const result{task.get()};
result->AcceptOwnershipOfSelf(std::move(task));
return result;
}
void Run()
{
// Do work ...
// Work is done. Self-destruct.
self_.reset();
}
private:
// Constructor needs to be private: Task must be created via `CreateTask`.
Task() = default;
void AcceptOwnershipOfSelf(std::unique_ptr<Task> self) { self_ = std::move(self); }
std::unique_ptr<Task> self_;
};
int main()
{
Task* const task{Task::CreateTask()};
std::ignore = std::async(&Task::Run, task);
std::this_thread::sleep_for(1s);
}哥德波特。请注意“毁灭”。只印了一次。
为什么不直接做然后再做呢?
如果self_.reset();要在对current_ptr进行零化之前删除以前托管的指针,那么self_仍然会指向~Task中的*this --导致无限循环。我们可以通过将self_.reset();替换为self_->~Task();来看到这一点。
当然,我们可以用以下两行代码替换self_.reset();:
std::unique_ptr<Task> tmp{std::move(self_)};
tmp.reset();我不知道委员会是否因为这个用例而决定像他们那样指定reset。
备注:
https://stackoverflow.com/questions/69006612
复制相似问题