Talel BELHAJSALEM
Talel BELHAJSALEM

Reputation: 4294

Destructor is not called for shared_ptr derived class

I want to implement the Mediator design pattern using shared_ptr for all the program.

Here is the Mediator interface:

class Mediator
{
    public:
        Mediator(){ print("Mediator()"); }
        virtual void Notify(std::shared_ptr<BaseComponent> sender) const = 0;
};

Here is the ExampleMediator:

class ExampleMediator : public Mediator
{
    private:
        std::shared_ptr<ExampleComponent> eC;
    public:
        void Notify(std::shared_ptr<BaseComponent> sender) const override {
            print("From Example Mediator");
        }
        void setExampleComponent(std::shared_ptr<ExampleComponent> eC_){
            eC = eC_;
        }
};

The ExampleMediator has a shared pointer to ExampleComponent and a method to set it.

This is the base class BaseComponent:

class BaseComponent
{
    protected:
        std::shared_ptr<Mediator> m;

    public:
        BaseComponent() { print("BaseComponent()"); }
        ~BaseComponent() { print("~BaseComponent()"); }
        void setMediator(std::shared_ptr<Mediator> m_){ m = m_; }
};

The BaseComponent has a shared pointer to the ExampleMediator and a method to set it.

Here is the ExampleComponent:

class ExampleComponent: public BaseComponent
{
    public:
        ExampleComponent(){ print("ExampleComponent()"); }
        ~ExampleComponent(){ print("~ExampleComponent()"); }
        void doSomethingOnDerived(){ print("ExampleComponent job");}
};

The main function:

int main()
{

    // Create the mediator
    auto mM = std::make_shared<ExampleMediator>();
    
    // Create the component
    auto eC = std::make_shared<ExampleComponent>();
    eC->setMediator(mM);
    
    // Set the component in the mediator
    mM->setExampleComponent(eC);
}

The output is:

Mediator()
BaseComponent()
ExampleComponent()

If I remove the line mM->setExampleComponent(eC); the constructors get called. Live code in Compiler Explorer: https://godbolt.org/z/E5ofEPGen

My goal is to use the components as shared pointers and not raw pointers, same for the Mediator.

What can be the cause for this issue?

Thanks.

Upvotes: 0

Views: 414

Answers (1)

eerorika
eerorika

Reputation: 238311

What can be the cause for this issue?

Shared pointers destroy their owned resource when the pointer is the last owner pointing to the resource. When local variable eC is destroyed on return of main, there is still another owner mM.eC so the resource won't be destroyed. Similarly, when the local mM is destroyed, its resource is still owned by the resource previously owned by eC.

When you own a resource, we typically consider the owner to "depend" on the resource. If we consider objects as nodes and dependency relations as edges, we get a directed graph. This dependency graph should not have loops, because that typically leads to the problem that you encountered.

With shared pointers, one way to break the loop is to weaken the ownership of one node. This can be done by using a weak pointer instead of a shared pointer. Note that you must then take care to handle the case where weakly owned resource has been destroyed before its dependee.

Upvotes: 2

Related Questions