Home > Blockchain >  Do I need a virtual destructor for a second interface class?
Do I need a virtual destructor for a second interface class?

Time:02-02

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) {
            callableB->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?

CodePudding user response:

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.

CodePudding user response:

It depends. In theory, Base doesn't need a virtual destructor. You need a destructor to be virtual when you lug around an object which has a different dynamic type than its static type.

In your example, you have Base pointer that really points to a Derivied object. If you were not to make ~Base() virtual, then, destroying that object would exhibit undefined behaviour -- likely by failing to destroy the Derived portion of the object.

So, as long as you don't plan to have an (owning!) pointer to your object through a particular base class, that base class's destructor need not be virtual.

  • Related