In this line:
m_pipeline.push_back(std::shared_ptr<System>(&sys));
You are creating a shared_ptr
for an already managed object, since you already wrapped the same object in another smart pointer. So you end up with two reference counts for the same object, thus you get a double free.
This is not how shared_ptr
should be used. Instead, you should return a shared_ptr
from addSystem
and take one as argument for addToPipeline
:
void Engine::addToPipeline(std::shared_ptr<System> sys)
{
m_pipeline.push_back(sys);
}
template <class T>
std::shared_ptr<T> Engine::addSystem()
{
std::shared_ptr<T> system = std::make_shared<T>();
m_systems.emplace(typeid(T),system);
return system; // No need to use the return value of emplace
}
The idea with shared_ptr
s is that instead of using bare pointers or references, you always pass a shared_ptr
(unless ownership doesn't matter - then you can also pass a reference). You have to do it this way because the reference counter is managed by the smart pointers.
Edit: As rozina pointed out: Of course you can still pass references to the managed object, as long as nobody tries to delete the corresponding address. This might actually be preferable if other code is interested in using a certain object, but not concerned about ownership. For example, you might want to have a public interface which allows obtaining a reference to some object that's managed by a smart pointer internally. For example:
class Foo {
public:
Bar& getBar() {
return *m_bar;
}
private:
std::shared_ptr<Bar> m_bar;
};
This is perfectly fine as long as nobody does delete &aFoo.getBar()
- which happens if you create a new shared_ptr
with that reference, as you did in your original code.