12 December 2011

Prohibiting inheritance

Sometimes we need some specific classes, which are better not used in other contexts but this very which we are trying to implement, so we need to make this class non-inheritable, i.e. no class can be derived from this one. To achieve this goal, we need to make the objects of the derived class non-constructible or, even less, non-destructible. The concept 'non-constructible' has more to care about than 'non-destructible', as the latter is directly connected to the only destructor of the class, and the former can be achieved only limiting all the constructors of the class, which also can be added through time. The only way we can prohibit access to some functions are access specifiers, of which only private makes sense here, as public and protected are always accessible by child classes. So now we came to making the destructor private. But in this case we cannot create an object as we cannot destruct it later. One thing we can do just to provide a static member which dynamically creates an object on a heap and returns the pointer. But as soon as we attempt to delete this pointer we will get a compile error again, as it will also try to call the destructor, which is private.

    To cope with this situation we can derive our class from another class, which in its turn has a private destructor, and declare our class as a friend of it, so that the destructor of our class will be able to call the destructor of the pivot class.

struct Helper
{
private:
    friend class Leaf;
    ~Helper();
};

class Leaf : public Helper
{
};

Now the destructor of Leaf has access to the destructor of Helper, but so do the derived classes of Leaf, as they also access the ~Helper() through ~Leaf(). To break this link we can inherit the Leaf from Helper virtually, in that case the most derived class will access the virtual base directly and will not have access.

struct Helper
{
private:
    friend class Leaf;
    ~Helper();
};

class Leaf : virtual public Helper
{
};

If you want to provide a common mechanism for any class, you better not inherit all such classes from the same Helper, as it is logically incorrect to link irrelevant classes in the same hierarchy, especially as some concrete types of more abstract one. So the helper classes should be different. Here we can take advantage of CRTP (curiously recurring template pattern), which creates a generic interface within different base classes for each derived class.

template <typename T>
class NonInheritable
{
private:
    ~NonInheritable();
    friend class T;
};

Now whenever you need to make a class non-inheritable you derive it from NonInheritable<T>, i.e.

class MyClass : virtual public NonInheritable<MyClass>
{
...
};

You can also inherit privately to keep this approach as an implementation detail, though MyClass is NonInheritable, so the inheritance logically should be public (as Stroustrup suggested use public inheritance in case of is-a relationship and a private one in case of has-a).

C++11 introduces a new keyword - final, which makes this peace of code redundant. So if you don't want any other class to inherit from your class, you just specify your class as final:

class NonInheritable final
{
    // class definition goes here
};

now any trial of inheritance will bring to a compile error.

No comments:

Post a Comment