许多关于 shared_ptr
的文章都警告不要意外地为同一对象创建独立的 shared_ptr
。
例如,this article .它有注释 //Bad, each shared_ptr thinks it's the only owner of the object
。
但如果这正是我想要的行为呢?例如:
auto* object = new Object();
auto ptr1 = std::shared_ptr<Object>(object);
auto ptr2 = std::shared_ptr<Object>(object, [ptr1](Object* obj){ obj->cleanup(); });
ptr2 = nullptr;
ptr1 = nullptr;
这在 GCC 6.3 上完美运行,但这样做是否合法,即标准是否允许这种用法?
最佳答案
让两个 shared_ptr 对象拥有同一个对象有时会奏效。它不起作用的地方是Object
。源自 std::enable_shared_from_this<Object>
.在这种情况下,分配给 shared_ptr 期间的魔法将导致您出现未定义的行为。
The constructors of std::shared_ptr detect the presence of an unambiguous and accessible (since C++17) enable_shared_from_this base and assign the newly created std::shared_ptr to the internally stored weak reference if not already owned by a live std::shared_ptr (since C++17). Constructing a std::shared_ptr for an object that is already managed by another std::shared_ptr will not consult the internally stored weak reference and thus will lead to undefined behavior.
https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
I don't want user to concurrently delete those objects from outside threads, so I'd like to use custom deleter that will merely schedule deleting.
解决方案将取决于清理操作是否需要共享计数(即是否需要超过一个滴答声)。
简单情况:
auto deleter = [&scheduler](Object* p)
{
auto delete_impl = [p]()
{
p->cleanup();
delete p;
};
scheduler.post(delete_impl);
};
auto po = std::shared_ptr<Object>(new Object(), deleter);
不太简单的情况:
在清理时间可能超过一个“滴答”的情况下,从 cppreference 的文档中我不清楚重新分配 p
是否有效到另一个shared_ptr<Object>
用于清理阶段。即使它严格来说是这样,它也是一个黑暗的角落,我不相信在所有库实现中标准化的行为。
为了安全起见,让我们定义一个新对象作为清理期间的共享句柄:
struct DyingObjectHandle : std::enable_shared_from_this<DyingObjectHandle>
{
DyingObjectHandle(Object* p) : p(p) {}
void cleanup()
{
auto self = shared_from_this();
... etc
}
void final_destroy()
{
delete p;
}
Object *p;
};
然后修改删除器:
auto deleter = [&scheduler](Object* p)
{
auto doh = std::make_shared<DyingObjectHandle>(p);
scheduler.post([doh = std::move(doh)]()
{
doh->cleanup();
});
};
auto po = std::shared_ptr<Object>(new Object(), deleter);
最后:
Actually library is a wrapper around boost::asio
这通常是常见低效率的根源。
安asio::io_context
通常应该将其视为整个应用程序的单例对象。它代表“应用程序范围的 IO 调度循环”。当 N 个线程运行相同时实现最大并发 io_context
, 每个启用 io 的对象都有自己的 strand
并且所有处理程序都通过链进行调度,例如:
timer_.async_wait(asio::bind_executor(my_strand_,
[self = shared_from_this()](error_code ec)
{
// ...handle the timer.
});
这样,在哪个线程处理程序上完成是无关紧要的。如果多个并发操作发生在同一个 io 对象上,它们将通过链进行序列化比它们都在同一个互斥量上竞争或绑定(bind)到特定线程的 io_context
时更有效 .
关于c++ - 让独立的 shared_ptr 指向同一个对象是否合法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54671719/