10

This example below illustrates how to prevent derived class from being copied. It's based on a base class where both the copy constructor and copy assignment operator are declared private.

class Uncopyable
{
protected:
   // allow construction and destruction of derived objects...
   Uncopyable() {}
   ~Uncopyable() {}

private:
   // but prevent copying...
   Uncopyable(const Uncopyable&);
   Uncopyable& operator=(const Uncopyable&);
};

We can use this class, combined with private inheritance, to make classes uncopyable:

class derived: private Uncopyable
{...};

Notice that the destructor in class Uncopyable is not declared as virtual.

Previously, I learned that

  • Destructors in base classes should be virtual.
  • Destructors in non-base classes should not be made virtual.

In this example, the destructor for Uncopyable is not virtual, but it is being inherited from. This seems to go against the wisdom I've learned previously.

When and why should destructor in base class NOT be defined as virtual?

j0k
  • 22,600
  • 28
  • 79
  • 90
Eric Z
  • 14,327
  • 7
  • 45
  • 69
  • You made Uncopyable. You didn't make Uninheritable. – Hans Passant Aug 10 '11 at 01:59
  • 1
    Tone down on the **HUGE BOLD** text, would you? It makes it annoying to read! – Praetorian Aug 10 '11 at 02:08
  • @Praetorian, Bold text helps organize your section clearly and make your question/answer more readable, (unless you're using a cellphone I guess?). Check out here: http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three – Eric Z Aug 10 '11 at 03:06
  • @Eric Your question didn't just have bold text, it had bold text in large font. When you do that all that is visible is a wall of black text, it detracts from the quality of the question. I suppose it is also a matter of personal preference, yet most posters on SO choose not to do this. Highlighting key sections in bold, italics, code section etc. is perfectly OK and necessary, but having several sentences in larger than usual bold font doesn't look very good. – Praetorian Aug 10 '11 at 05:32
  • @Praetorian, thanks, man. I'll keep your words in mind:) – Eric Z Aug 10 '11 at 06:56

5 Answers5

12

A base class destructor only needs to be virtual if you might try deallocating an object of a derived type through a pointer of the base type. Consequently, if you only inherit from the base class privately instead of publicly, as would be the case in Uncopyable, then you don't need to worry about putting in a virtual destructor, because when using private inheritance you can't get a pointer to the derived object and store it in a pointer to the base type.

Another example might be if you were to use a mixin class like this one that makes a class track the number of object allocations, where the mixin is inherited from to acquire behavior but not to be treated polymorphically:

template <typename T> class Counter {
public:
    Counter() { ++numInstances; }
    Counter(const Counter&) { ++numInstances );
    ~Counter() { --numInstances; }

    static unsigned getNumInstances() { return numInstances; }

private:
    static unsigned numInstances;
}
template <typename T> unsigned Counter<T>::numInstances = 0;

More generally, when using static polymorphism, you don't need virtual destructors because you never treat the classes polymorphically using pointers to the base type. You only use a pointer to the derived type.

There are probably a few other cases I didn't cover here, but these two (private inheritance, mixin classes, and static polymorphism) cover much of the space where virtual destructors aren't required.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • The funny part is that boost isn't encouraging private inheritance from `noncopyable`. On the other hand using `noncopyable&` or `noncopyable*` seems so pointless that surely no one is ever going to do it, right? – pmr Aug 10 '11 at 02:08
  • I've never seen anyone try to do this... I have no idea why you would! – templatetypedef Aug 10 '11 at 02:11
  • Another reason to not make the destructor virtual in this particular case (`Uncopyable` or `boost::noncopyable`) is to not impose a vtable on every class that wants to make use of it. – Praetorian Aug 10 '11 at 02:12
  • 2
    Note though that if your destructor is not virtual, it should be `protected` in such cases. That way, even idiots can't delete through the base class pointer. – Billy ONeal Aug 10 '11 at 02:14
  • With Clang: `-Wdelete-non-virtual-dtor` (present in `-Wmost` and `-Wall`) warns when using `delete` on a class with virtual functions but no virtual destructor. Still imperfect, obviously, as it does not cover the use of `Uncopyable`. – Matthieu M. Aug 10 '11 at 07:03
2

When you design the base not as interface, but as implementation detail (note the private inheritance from Uncopyable).

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
1

You technically don't have to make your decostructor virtual if you know nobody will delete it as a Uncopyable*, but will always delete it as a subclass of the same.

Yes this is essentially, what @templatetypedef said, but I'm going to explain it in a maybe easier way.

So: if people might do something like this:

void aFunction(Uncopyable* obj) {
    delete obj;
}

Then you should declare your destructor virtual (to make sure potential subclasses get their destructor called.

However, if you know people will be deleting Subclasses like so:

class Widget : public Uncopyable
{
  ....
};

void aFunction(Widget* obj) {
   delete obj;
}

Then you don't have to make your destructor virtual (as the subclasses destructor will be called).

At least, that's my understanding.

Attila
  • 28,265
  • 3
  • 46
  • 55
RyanWilcox
  • 13,890
  • 1
  • 36
  • 60
0

When you need your objects to be plain old data, with no vtable. I'd comment the heck out of it if I ever needed it, as 99% of the time leaving off the 'virtual' in base class destructors is simply a mistake that someone will want to correct.

Graham Perks
  • 23,007
  • 8
  • 61
  • 83
  • When you need your objects to be plain old data, you generally aren't using inheritance. (Namely, because any class with a base is not a POD type) – Billy ONeal Aug 10 '11 at 02:16
0

The general rule is to make your destructor public and virtual, or protected and non-virtual. In the first case, your object use destroyable polymorphically and teh virtual destructor will do the right thing. In the second case it will only be destroyed as the actual child class and still do the right thing.

Mark B
  • 95,107
  • 10
  • 109
  • 188