C.hier: Class hierarchies (OOP)[1]

C.121: If a base class is used as an interface, make it a pure abstract class

  • Reason
    A class is more stable (less brittle) if it does not contain data. Interfaces should normally be composed entirely of public pure virtual functions and a default/empty virtual destructor.Example
public:
    // ...only pure virtual functions here ...
    virtual ~My_interface() {}   // or =default
};
Example, bad
class Goof {
public:
    // ...only pure virtual functions here ...
    // no virtual destructor
};

class Derived : public Goof {
    string s;
    // ...
};

void use()
{
    unique_ptr<Goof> p {new Derived{"here we go"}};
    f(p.get()); // use Derived through the Goof interface
    g(p.get()); // use Derived through the Goof interface
} // leak

The Derived is deleted through its Goof interface, so its string is leaked. Give Goof a virtual destructor and all is well.

  • Enforcement
    Warn on any class that contains data members and also has an overridable (non-final) virtual function that wasn’t inherited from a base class.

  • 相关Items:
    Effiective C++ 3rd edition.Item 7: Declare destructors virtual in polymorphic base classes.
    C++ Coding Standards: 101 Rules, Guidelines, and Best PracticesItem 50: Make base class destructors public and virtual, or protected and nonvirtual.

Reason from stackoverflow
Virtual destructors are useful when you might potentially delete an instance of a derived class through a pointer to base class:

class Base 
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};

Here, you'll notice that I didn't declare Base's destructor to be virtual. Now, let's have a look at the following snippet:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

Since Base's destructor is not virtual and b is a Base* pointing to a Derived object, delete b has undefined behaviour:

[In delete b], if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.

In most implementations, the call to the destructor will be resolved like any non-virtual code, meaning that the destructor of the base class will be called but not the one of the derived class, resulting in a resources leak.

To sum up, always make base classes' destructors virtual when they're meant to be manipulated polymorphically.

If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete on a base class pointer.

You can learn more about virtuality and virtual base class destructor in this article from Herb Sutter.

推荐阅读更多精彩内容