effect of both base and derived virtual destructors
Asked Answered
P

2

6

I've had some second thoughts on multiple virtual destructors, esp. after reading reading http://blogs.msdn.com/b/oldnewthing/archive/2004/05/07/127826.aspx .

Suppose I have

class Base
{
    public:
        Base();
        virtual ~Base();

    private:
        Logger* _logger;
};

//and

class Derived : public Base{
    public:
        Derived();
        virtual ~Derived();

    private:
        Logger* _logger;
};

in the cpp files, in each destructor I am deleting the respective _logger pointers

Base::~Base(){  //base.cpp
    delete _logger;
}
Derived::~Derived(){ //derived.cpp
    delete _logger;
}

will this work as I intended, without memory leaks?

Pithy answered 17/11, 2012 at 0:25 Comment(4)
That should work as long as you keep the Base logger private.Dizzy
In the code shown both _logger pointers will have indeterminate values, because they're not properly initialized.Boatbill
@Boatbill - both _logger are initialized in the respective .cpp filesPithy
@Pithy Erm. You're completely right, I don't know how I overlooked that possibility :_Boatbill
I
5

First off, if you make the base class destructor virtual, all derived classes will automatically get a virtual destructor if you declare them as virtual or not. This is generally true for matching signatures: if a base class has a virtual function with the same signature as a function in a derived class, the function in the derived class is an override and is virtual (although in C++ 2011 you can prevent further overriding using the final keyword in which case another override would create an error).

That said, destructors are special: when you make a destructor virtual it will still be called even if there is another overriding destructor! The only impact of a destructor being virtual is what happens if you delete an object using a pointer to a base class when the object actually happens to be of a derived type: If the destructor isn't virtual you get undefined behavior while the Right Thing happens if the destructor is virtual. For example:

class not_a_base {};
class bad_idea: public not_a_base {};

class a_base { public: virtual ~a_base() {} };
class ok: public a_base {};

int main() {
    a_base* ab = new ok;
    delete ab; // <---- all is good here!

    not_a_base* nab = new bad_idea;
    delete nab; // <---- results in undefined behavior
}

The reason destructors are not virtual by default is simply that this would mean that object size is always increased by a word size which is unacceptable in general.

Indult answered 17/11, 2012 at 0:48 Comment(1)
Thanks for clarifying exactly when issues could occur.Pithy
T
4

Base::_logger is a different variable then Derived::_logger. So you should delete Derived::_logger in Derived's dctor, or else you leak memory.

Note that this has nothing to with it being private. Consider this example program:

#include <iostream>

class A {
public:
    bool foo;
};

class B: public A {
public:
    bool foo;
};

int main()
{
    B b;
    std::cout << &b.B::foo << ' ' << &b.A::foo << '\n';
}

The addresses are different. That means they're different variables, even though they have the same name. This is possible since each class introduces its own namespace, so the names don't really clash. The first is A::foo, the other one B::foo.

Since your destructors are virtual, both will get called, and the correct _logger will be deleted in both of them.

Turnbow answered 17/11, 2012 at 0:44 Comment(1)
"Since your destructors are virtual, both will get called, and the correct _logger will be deleted in both of them." Thanks for the confirmation =)Pithy

© 2022 - 2024 — McMap. All rights reserved.