13
/*Child is inherited from Parent*/
class Parent {  
  public:  
    Parent () //Constructor
    {
        cout << "\n Parent constructor called\n" << endl;
    }
  protected:
    ~Parent() //Dtor
    {
        cout << "\n Parent destructor called\n" << endl;
    }
};

class Child : public Parent 
{
  public:
    Child () //Ctor
    {
        cout << "\nChild constructor called\n" << endl;
    }
    ~Child() //dtor
    {
        cout << "\nChild destructor called\n" << endl;
    }
};

int main ()
{
    Parent * p2 = new Child;          
    delete p2;
    return 0;
}

If I make Parent's destructor virtual, then I obtain an error, so what is the purpose of making a protected destructor virtual?

j0k
  • 22,600
  • 28
  • 79
  • 90
tusharfloyd
  • 1,732
  • 3
  • 14
  • 18
  • 3
    Maybe we should start with "why would you make dtor protected?". – Cat Plus Plus Jan 23 '12 at 10:53
  • 4
    Why did you ever want to make the destructor virtual? Shouldn't *you* know the purpose? A protected destructor means that objects shouldn't be destructed through base pointers, so the code in `main` is plain wrong. – thiton Jan 23 '12 at 10:53
  • See http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors – user998692 Jan 23 '12 at 10:57
  • I understand the use of virtual dtors and the use of protected dtors but i saw some code with virtual protected dtors. What i did not understand was when both are used what effect will it creates? – tusharfloyd Jan 23 '12 at 11:06
  • @CatPlusPlus: by making dtors protected you will prevent creation of base class objects on stack. am i rite? – tusharfloyd Jan 23 '12 at 11:10

5 Answers5

19

Just to give one example: Say you have an base class which implements reference counting. You have an addRef and a release method and you want your object to be destroyed, if (and only if) the internal counter reaches zero through a call to release.

So, first you want your destructor protected (since you only want to destroy the object from within release).

If you plan to derive from your class, you also want to have your destructor virtual, since you need a virtual destructor whenever you want to destroy a child object through a pointer to a base class (thanks @sharptooth for the hint ...)

Evg
  • 25,259
  • 5
  • 41
  • 83
MartinStettner
  • 28,719
  • 15
  • 79
  • 106
  • 3
    No, you need a virtual destructor regardless of whether derived classes require any extra destruction, otherwise behavior is just undefined. – sharptooth Jan 23 '12 at 11:14
  • I saw some code that uses this trick to force all destruction to go through friend C-style wrapper function (defined per derived class). I guess the intent was similar but was lost under maintainence. – Muxecoid Nov 19 '12 at 14:35
  • 2
    @MartinStettner See my answer: a protected destructor doesn't need to be virtual. – Alexandre Hamez Aug 23 '18 at 11:30
  • You need a virtual destructor even when you want to destroy a derived object **through the derived class pointer**. Deletion is implemented in the base class as `delete this`, so the type of `this` will be `Base*` even when doing `Derived* foo = new Derived`. So if the destructor is not virtual, only the Base class' destructor would be invoked. – wmjdgla Jun 28 '22 at 14:40
15

There's an entry in the C++ Core Guidelines dedicated to this specific subject:

C.35: A base class destructor should be either public and virtual, or protected and nonvirtual

Reason To prevent undefined behavior. If the destructor is public, then calling code can attempt to destroy a derived class object through a base class pointer, and the result is undefined if the base class’s destructor is non-virtual. If the destructor is protected, then calling code cannot destroy through a base class pointer and the destructor does not need to be virtual; it does need to be protected, not private, so that derived destructors can invoke it. In general, the writer of a base class does not know the appropriate action to be done upon destruction.

So, the destructor doesn't need to be virtual if it's protected. However, there is an exception:

Exception We can imagine one case where you could want a protected virtual destructor: When an object of a derived type (and only of such a type) should be allowed to destroy another object (not itself) through a pointer to base. We haven’t seen such a case in practice, though.

So, to sum up, in practice a protected destructor does not need to be virtual.

Alexandre Hamez
  • 7,725
  • 2
  • 28
  • 39
  • libhdf5 uses a virtual protected destructor in H5Object. I don't know if that is a valid example or just a mistake though. – MaxNoe Nov 28 '20 at 21:06
  • You missed another set of exceptions further down in the discussion section: "Some component architectures (e.g., COM and CORBA) don't use a standard deletion mechanism, and foster different protocols for object disposal. Follow the local patterns and idioms, and adapt this guideline as appropriate." MartinStettner's answer is basically describing COM and is thus acceptable under the Guidelines. – wmjdgla Jun 28 '22 at 13:03
6

Yes, if you intend to do delete this in class Parent member functions which is very common when implementing IUnknown::Release() in COM objects.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
4

protected: Base::~Base(); should be virtual at least if you (plan on) deleting any objects derived from Base within Base or a derived class of Base.

bitmask
  • 32,434
  • 14
  • 99
  • 159
  • 1
    @user1085822: So, you're thanking me while unaccepting my answer. What are you trying to tell me? – bitmask Jan 23 '12 at 12:05
  • Shouldn't this be just – ksb Oct 04 '16 at 08:52
  • Shouldn't this be just - "protected: Base::~Base(); should be virtual at least if you (plan on) deleting any objects derived from Base within Base"? Why the "or a derived class of Base". part? – ksb Oct 04 '16 at 09:01
0

MartinStettner's answer basically describes COM. To make things clearer, consider the following code (referenced from Microsoft's Implementing IUnknown in C++):

#include <Windows.h>

class Base
{
public:
    Base() = default;

    ULONG AddRef() { return InterlockedIncrement(&m_cRef); }

    ULONG Release()
    {
        const ULONG cRef = InterlockedDecrement(&m_cRef);
        if (0 == cRef)
        {
            delete this;
        }
        return cRef;
    }
protected:
    virtual ~Base() { printf(__FUNCTION__ "\n"); }
    ULONG m_cRef{1};
};

class Derived : public Base
{
public:
    Derived() = default;
protected:
    ~Derived() { printf(__FUNCTION__ "\n"); }
};

int main()
{
    Base* bar = new Derived;
    //delete bar; // C2248: 'Base::~Base': cannot access protected member declared in class 'Base'
    bar->Release();

    Derived* foo = new Derived;
    //delete foo; // C2248: 'Derived::~Derived': cannot access protected member declared in class 'Derived'
    foo->Release();

    return 0;
}

Output:

Derived::~Derived
Base::~Base
Derived::~Derived
Base::~Base

If virtual was removed from ~Base(), the output would be missing both instances of Derived::~Derived, which is expected since the type of this in Base's delete this is of Base* type.

So if you don't want callers to do delete bar/delete foo and want both base and derived classes' destructors to be invoked, both destructors must be protected and at least the Base class' destructor must be virtual.

wmjdgla
  • 335
  • 2
  • 10