【问题标题】:How to fix this shared_ptr reference cycles?如何修复这个 shared_ptr 参考周期?
【发布时间】:2021-06-15 03:05:41
【问题描述】:

我设计了一个App,其中包含一堆layers 和一个活动的obj。 当Layer 附加到App 时,Layer 告诉App 什么是活动的object。但是我的设计在解除分配时会导致一个信号陷阱。

它会导致 sigtrap,因为 App 中 shared_ptr<Obj> m_obj 的破坏首先发生,这会将 use_count 减少到 1。然后调用 onDetech 函数,将活动的 shared_ptr<Obj> m_obj 设置为 nullptr 并减少 use_count为0!但是layer 仍然拥有shared_ptr<Obj>

下面的代码是重现的最小示例。我注意到我的代码有一个引用循环,但我不知道如何解决这个问题,除非在 App 类中使用 obj 的原始指针。

我在App 中使用shared_ptr 代替obj,因为App 拥有obj 的共享所有权对我来说是有意义的。在这种情况下我不应该使用shared_ptr吗?

class App;

class Obj {
   public:
    ~Obj() { std::cout << "obj destruct" << std::endl; }
};

class Layer {
   public:
    Layer() { m_obj = std::make_shared<Obj>(); }

    ~Layer() { std::cout << m_obj.use_count() << std::endl; }

    void onAttach(App *app);

    void onDetach();

    std::shared_ptr<Obj> m_obj;

   private:
    App *m_app;
};

class LayerStack {
   public:
    void pushLayer(App *app, std::shared_ptr<Layer> layer) {
        m_layers.push_back(layer);
        layer->onAttach(app);
    }

    ~LayerStack() {
        for (auto &layer : m_layers) {
            layer->onDetach();
        }
    }

   private:
    std::vector<std::shared_ptr<Layer>> m_layers;
};

class App {
   public:
    App() {
        m_defaultLayer = std::make_shared<Layer>();
        m_stack.pushLayer(this, m_defaultLayer);
    }

    ~App() {}
    LayerStack m_stack;
    std::shared_ptr<Layer> m_defaultLayer;
    std::shared_ptr<Obj> m_activeObj;
};

void Layer::onAttach(App *app) {
    m_app = app;
    app->m_activeObj = m_obj;
    std::cout << m_obj.use_count() << std::endl;
}

void Layer::onDetach() {
    m_app->m_activeObj = nullptr;
    std::cout << m_obj.use_count() << std::endl;
}

int main() {
 A a;
}

输出:

2
obj destruct
-923414512
-923414512

【问题讨论】:

  • 感谢您的建议。我已经更新了我的代码示例。但是我描述的问题仍然存在。 use_count 减少为负数。
  • 它并没有减少到负数,你只是在阅读垃圾。 Address Sanitizer 免费后可以找到你用过的地方:godbolt.org/z/s1YxbsWdc

标签: c++ shared-ptr weak-ptr


【解决方案1】:

您在 m_activeObj 的生命周期结束后访问它,因此您的程序的行为是未定义的。

事件顺序如下:

  1. App 对象超出范围
  2. ~App 运行
  3. m_activeObj 被销毁;在此之后,它的生命周期结束,无法再访问
  4. m_defaultLayer 被销毁
  5. m_stack 被销毁
    1. m_layers[0].onDetach() 被调用
    2. onDetachm_app-&gt;m_activeObj 设置为 nullptr,但其生命周期已经结束,因此行为未定义。
  6. 其他无关的东西;你已经完蛋了。

解决方案是对事物进行重新排序,这样您就不会在其生命周期结束后访问m_activeObj。要么将 m_stack 的声明移到 m_activeObj 之后,使其首先被销毁,要么在 ~App 中手动清除它。

【讨论】:

  • 哦..好的,我现在看到了。还有一个问题,为什么我在引用一个已销毁的对象时没有收到 sigfault?
  • 因为未定义的行为是未定义的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-27
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多