A class designed to be inherited-from is called a Base class. Care should be taken with the special member functions of such a class.
A class designed to be used polymorphically at run-time (through a pointer to the base class) should declare the destructor virtual
. This allows the derived parts of the object to be properly destroyed, even when the object is destroyed through a pointer to the base class.
class Base {
public:
virtual ~Base() = default;
private:
// data members etc.
};
class Derived : public Base { // models Is-A relationship
public:
// some methods
private:
// more data members
};
// virtual destructor in Base ensures that derived destructors
// are also called when the object is destroyed
std::unique_ptr<Base> base = std::make_unique<Derived>();
base = nullptr; // safe, doesn't leak Derived's members
If the class does not need to be polymorphic, but still needs to allow its interface to be inherited, use a non-virtual protected
destructor.
class NonPolymorphicBase {
public:
// some methods
protected:
~NonPolymorphicBase() = default; // note: non-virtual
private:
// etc.
};
Such a class can never be destroyed through a pointer, avoiding silent leaks due to slicing.
This technique especially applies to classes designed to be private
base classes. Such a class might be used to encapsulate some common implementation details, while providing virtual
methods as customisation points. This kind of class should never be used polymorphically, and a protected
destructor helps to document this requirement directly in the code.
Finally, some classes may require that they are never used as a base class. In this case, the class can be marked final
. A normal non-virtual public destructor is fine in this case.
class FinalClass final { // marked final here
public:
~FinalClass() = default;
private:
// etc.
};