如何修复此 shared_ptr 个引用循环?

How to fix this shared_ptr reference cycles?

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

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

下面的代码是一个最小的重现示例。我注意到我的代码有一个引用循环,但我不知道如何解决这个问题,除了在 App class.

中使用 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

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

事件顺序如下:

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

解决方案是重新排序,这样您就不会在其生命周期结束后访问 m_activeObj。要么将 m_stack 的声明移动到 m_activeObj 之后,以便它首先被销毁,要么在 ~App.

中手动清除它