【问题标题】:A bug in std::shared_ptr?std::shared_ptr 中的错误?
【发布时间】:2017-10-28 15:02:30
【问题描述】:

执行以下程序时会发生什么?

#include <iostream>
#include <memory>

class test;
std::shared_ptr<test> a_test_object;
struct test
{
    ~test()
    {
        std::cout << "destroy test" << std::endl;
        auto ptr = a_test_object;
    }
};

int main()
{
    a_test_object = std::make_shared<test>();
    //a_test_object.reset();  // Uncomment this and it works fine.
}

我在 GCC 和 Visual Studio 2015 上对此进行了测试,在这两种情况下程序都崩溃了。发生的情况是共享指针在其析构函数中递减计数,然后执行 ~test(),它复制共享指针递增然后递减计数,触发对 ~test() 的无限递归调用。奇怪的是,调用 reset() 不会触发问题。

我今天遇到了这个问题,因为一些使用 pre-C++11 版本的 shared_ptr 的旧代码(没有这种双重删除错误)已更新为使用 std::shared_ptr。令我惊讶的是,std::shared_ptr 使程序崩溃。这真的是 std::shared_ptr 的预期行为吗?

【问题讨论】:

  • 进入析构函数~test的唯一方法是a_test_object的生命周期已经结束,因此在其生命周期结束后访问该对象是UB。
  • 我没有看到这里的好战角度。在这种情况下,您希望 shared_ptr 做什么?
  • 我不明白它是如何工作的。进入析构函数后,shared_ptr 控制块上的引用计数为 0。然后将其分配给本地 ptr 变量,该变量再次将引用计数增加到 1。当它超出范围时,计数再次降至 0,您将获得第二个空闲。 shared_ptr 无法知道你在搞恶作剧。
  • @DavisKing "std::shared_ptr 可以处理指向其他事物的复杂事物模式" - 当然不能,不是一般性。它可以提供共享所有权,仅此而已。
  • @DavisKing:“我的意思是,我们告诉人们 std::shared_ptr 可以处理指向其他事物的复杂模式。”不,我们没有。复杂的指向模式通常会导致 循环引用shared_ptr 绝对无法处理。

标签: c++ c++11 shared-ptr object-lifetime


【解决方案1】:

您违反了 C++ 对象生命周期的基本规则:对象生命周期结束后,您无法访问该对象。只有在所有shared_ptr 实例的生命周期结束后,才能删除test。因此,它的析构函数以及它调用的所有代码都无法访问任何引用自身的shared_ptr 实例。

否则会调用未定义的行为。这不是shared_ptr 中的错误;这是您代码中的错误。如果testvector&lt;test&gt; 中的一个对象并尝试在您的析构函数中访问该vector&lt;test&gt; 实例,那么它的UB 一样多。

令人恼火的是,可以使工作正常的东西却不是出于迂腐的原因。

这不是“迂腐的理由”。这是不可能a_test_object 不存在了;它不是一个有效的对象。

它不起作用的原因与返回对堆栈变量的引用不起作用的原因相同。对象此时已消失;你不能再访问它了。

所以你需要重构你的代码来处理这个事实。

【讨论】:

  • 对象的生命周期结束后,您将无法访问它。”这是库规则还是核心语言规则?
  • @curiousguy:由于a_test_object 的析构函数被shared_ptr 的析构函数调用,因此shared_ptr 实例的进一步使用属于[class.cdtor]。语言规则最终归结为“它取决于对象”。而且由于该标准没有定义其当前正在销毁的库对象的状态,因此它们的状态根据定义是未定义的。因此,访问它们会导致未定义的行为。
  • 您的评论比您的回答更准确。 shared_ptr 的析构函数正在执行中。它的成员肯定完好无损。它的存储肯定完好无损。声称“它不再存在”和“它不是一个有效的对象”似乎不太正确。观察你在 dtor执行期间可以做的有限事情(正如你在评论中提到的那样)更具说服力。考虑重写? :)
猜你喜欢
  • 1970-01-01
  • 2012-02-06
  • 1970-01-01
  • 2017-07-20
  • 2016-12-27
  • 2020-12-03
  • 1970-01-01
  • 1970-01-01
  • 2014-10-04
相关资源
最近更新 更多