Reputation: 104
I designed an App
that holds a stack of layers
and an active obj
.
When a Layer
is attached to the App
, the Layer
tells App
what an active object
is. But my design causes a sigtrap when deallocating.
It cause sigtrap because the destruction of shared_ptr<Obj> m_obj
in App happens first which reduce the use_count
to 1. Then the onDetech
function gets call, setting the active shared_ptr<Obj> m_obj
to nullptr
and reduce use_count
to 0! But the layer
still holds an shared_ptr<Obj>
.
The code below is a minimal example to reproduce. I notice my code has a reference cycle but I have no idea how to fix this except using a raw pointer for obj
in the App
class.
I used shared_ptr
for obj
in App
because it makes sense to me that App
has the shared ownership of obj
. Should I not use shared_ptr
in this case?
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;
}
output:
2
obj destruct
-923414512
-923414512
Upvotes: 0
Views: 193
Reputation: 30619
You're accessing m_activeObj
after its lifetime has ended, and thus the behavior of your program is undefined.
The sequence of events is as follows:
App
object goes out of scope~App
runsm_activeObj
is destroyed; after this its lifetime has ended and it can no longer be accessedm_defaultLayer
is destroyedm_stack
is destroyed
m_layers[0].onDetach()
is calledonDetach
sets m_app->m_activeObj
to nullptr
, but its lifetime has already ended, so behavior is undefined.The solution is to reorder things so that you don't access m_activeObj
after its lifetime has ended. Either move m_stack
's declaration after m_activeObj
so it gets destroyed first or clear it manually in ~App
.
Upvotes: 2