Как исправить этот ссылочный цикл shared_ptr?

#c #shared-ptr #weak-ptr

Вопрос:

Я разработал App устройство, которое содержит стопку layers и активный obj . Когда a Layer присоединен к App , то Layer говорит App , что такое активный object . Но мой дизайн вызывает sigtrap при освобождении.

Это вызывает sigtrap, потому shared_ptr<Obj> m_obj что сначала происходит разрушение в приложении, которое уменьшает значение use_count до 1. Затем onDetech функция получает вызов, устанавливая активное shared_ptr<Obj> m_obj значение nullptr и уменьшая use_count его до 0! Но тот layer все еще держится shared_ptr<Obj> .

Приведенный ниже код является минимальным примером для воспроизведения. Я замечаю, что в моем коде есть ссылочный цикл, но я понятия не имею, как это исправить, кроме как использовать необработанный указатель для obj в App классе.

Я использовал shared_ptr для obj in App , потому что для меня имеет смысл, что 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 amp;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
 

Комментарии:

1. Спасибо за ваше предложение. Я обновил свой пример кода. Но проблема, которую я описал, все еще существует. Значение use_count уменьшается до отрицательного.

2. Это не уменьшилось до отрицательного, вы просто читаете мусор. Адрес дезинфицирующего средства можно найти там, где вы пользовались после бесплатного: godbolt.org/z/s1YxbsWdc

Ответ №1:

Вы получаете доступ m_activeObj после истечения срока его действия, и, следовательно, поведение вашей программы не определено.

Последовательность событий выглядит следующим образом:

  1. App объект выходит за рамки
  2. ~App бежит
  3. m_activeObj уничтожается; после этого его срок службы заканчивается, и к нему больше нельзя получить доступ
  4. m_defaultLayer уничтожается
  5. m_stack уничтожается
    1. m_layers[0].onDetach() называется
    2. onDetach устанавливается m_app->m_activeObj в nullptr значение , но его срок службы уже истек, поэтому поведение не определено.
  6. Неуместные другие вещи; ты уже облажался.

Решение состоит в том, чтобы изменить порядок вещей, чтобы вы не получали доступ m_activeObj к ним после истечения срока их службы. Либо переместите m_stack объявление после m_activeObj , чтобы оно было уничтожено первым, либо очистите его вручную ~App .

Комментарии:

1. О… хорошо, теперь я это понимаю. Еще один вопрос, почему я не получаю sigfault при ссылке на уничтоженный объект?

2. Потому что неопределенное поведение не определено.