Presenting the Problem
The Standard Template Library isn't an object-oriented framework. Rather, it's an object-based paradigm, whereby a typical class, say
std::string, is an autonomous and complete entity. Implementation-wise, an object-based design is characterized by the following:
- The class has a non-virtual destructor
- The class doesn't have any virtual member functions
- The class doesn't have any base classes
- The class doesn't contain any protected members
These telltale properties indicates that a certain class shouldn't be derived from. And yet, the compiler won't stop programmers from doing this. This is probably why many programmers, not fully aware of the risks, still attempt to derive from
std::string, extending its functionality by adding a
const char * conversion operator, or a
trim() member function, etc. Trouble begins when you have a pointer to
std::string that is bound to a derived object.
When the said pointer is deleted, the derived subobject isn't properly
destroyed, causing undefined behavior. In the case of Standard Library containers, there's not much that you can do to enforce the non-inheritable constraint. However, if you're designing an application or a library that has non-inheritable classes, you can disable further derivation restricting their constructor(s) access.
Use Private Constructors
To enforce the non-inheritable constraint for a given class, declare at least one constructor explicitly as a private member (otherwise, the compiler will implicitly it as public). Now, any attempt to derive from this class will cause a compilation error:
class noninheritable
{
private: //all ctors are private to disable subclassing
explicit noninheritable(int n);
explicit noninheritable(const char *);
};
class derived: public noninheritable
{
public:
derived(): noninheritable(0){} //error:
//ctor is inaccessible
};