c++ - 让独立的 shared_ptr 指向同一个对象是否合法?

标签 c++ shared-ptr

许多关于 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/

相关文章:

c++ - 将 boost::shared_ptr 与重载下标运算符 ([]) 的类一起使用

c++ - 从存储对象的 vector 访问变量

c++ - Apache thrift 和 cpp 代码生成

c++ - 在 C++0x 中传递/移动构造函数的参数

c++ - <?= operator C++ greater less 问号等于号

c++ - 超出范围后在 Lambda 中设置共享指针

c++ - 存储派生对象的映射

C++17 std::shared_ptr<> 为类数组对象重载 operator[]

c++ - GMock StrictMock无趣的函数调用不会通过测试

c++ - 使用 C++ 将图像写入 RabbitMQ 队列