Do I need a virtual destructor for a second interface class?

Advertisements

I’ve got classes called "Base" and "Derived".

struct Base {
    Base() = default;
    virtual ~Base() = default;
    Base(const Base&) = delete;
    Base& operator=(const Base&) = delete;

    virtual void DoStuff() = 0;
};

The "Base" class needs virtual destructor and it’s understandable. Also I don’t allow copying of this class

struct Derived : Base {
    Derived() = default;
    ~Derived() override = default;

    void DoStuff() override { /*...*/ }
};
int main()
{
    std::shared_ptr<Base> a = std::make_shared<Derived>();
    a->DoStuff();
    return 0;
}

Now let’s introduce other classes called, I don’t know, Callable and DerivedCallable

struct Callable
{
    virtual void Call() = 0;
};
struct DerivedCallable : Base, Callable
{
    DerivedCallable() = default;
    ~DerivedCallable() override = default;

    void DoStuff() override { /*...*/ }
    void Call() override { /*...*/ }
};
int main()
{
    std::shared_ptr<Base> a = std::make_shared<Derived>();
    a->DoStuff();

    {
        auto callableA = std::dynamic_pointer_cast<DerivedCallable>(a);
        if(callableA) {
            callableA->Call();
        }
    }

    std::shared_ptr<Base> b = std::make_shared<DerivedCallable>();
    b->DoStuff();
    
    {
        auto callableB = std::dynamic_pointer_cast<DerivedCallable>(b);
        if(callableB) {
            callableA->Call();
        }
    }

    return 0;
}

Derived does not inherit from Callable, so callableA is nullptr, thus the if statement won’t execute the Call() function.

DerivedCallable on the other hand inherits from Callable and the std::dynamic_pointer_cast will increase the ref count of the object to 2, so when the callableB gets out of the scope the object won’t be deallocated, only the refcount will decrease to 1 and then the main function will deallocate b.

Does Callable need to have a virtual destructor?

>Solution :

You only need a virtual destructor if you are ever going to delete a derived class object through a base class pointer.

Since you are using std::shared_ptr, you don’t need any virtual destructors because the shared_ptr stores a correctly typed deleter (no matter how you cast it).

If you are ever going to own a DerivedCallable object with a Callable pointer (std::unique_ptr<Callable> or something else that calls delete on a Callable*), then it should have a virtual destructor. However, if you only ever have non-owning references with Callable*, then you don’t strictly need the virtual destructor.

Adding a virtual destructor when the class already has other virtual members is very cheap, so it’s fine to add it so that you don’t have to worry about accidentally delete-ing it wrong.

Leave a Reply Cancel reply