关于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上可以完美运行,但是这样做合法吗?(例如,标准允许使用吗?
)答案 0 :(得分:1)
具有两个拥有相同对象的shared_ptr对象有时会起作用。 Object
源自std::enable_shared_from_this<Object>
的地方不起作用。在这种情况下,分配给shared_ptr的过程中的魔力将导致您不确定的行为。
std :: shared_ptr的构造函数检测到明确且可访问的(自C ++ 17起)enable_shared_from_this基的存在,并将新创建的std :: shared_ptr分配给内部存储的弱引用(如果尚未由实时std拥有) :: shared_ptr(自C ++ 17起)。为已经由另一个std :: shared_ptr管理的对象构造一个std :: shared_ptr不会参考内部存储的弱引用,从而导致不确定的行为。
https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
我不希望用户从外部线程中同时删除那些对象,所以我想使用仅计划删除的自定义删除器。
解决方案将取决于清理操作是否需要共享计数(即,是否需要花费超过一滴答的时间)。
简单情况:
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);
不太简单的情况:
在清理过程可能要花费一个“ tick”以上的时间的情况下,我从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);
最后:
实际上,库是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对象上发生多个并发操作,则与通过同一个互斥对象竞争或绑定到特定线程的io_context
相比,它们将通过链更有效地进行序列化。 / p>
答案 1 :(得分:0)
这是合法的。唯一不合法的事情是双重删除指向的对象。您可以通过让一个shared_ptr
使用自定义删除器来防止这种情况。
这是好习惯吗?它会通过代码审查吗?它会引起眉毛吗?您自己决定。
我会努力不使用这样的结构。
答案 2 :(得分:0)
您显示的内容似乎是合法的。
我不希望用户从外部线程中同时删除那些对象,所以我想使用仅计划删除的自定义删除器。
我建议使用另一种方法:只需将一个共享指针级别与一个自定义删除器一起使用。在删除器中,将指针添加到线程安全队列中,以在正确的线程中销毁该队列。一种简单的方法是将唯一的指针存储在队列中,然后只需清除队列以释放内存即可。