Analyzing the problem
The problem is very simple: Create a mechanic whereby it's possible to mark a class (say,NotABase
) As being uninheritable, such that attempting to derive a class from that uninheritable class results in a compile-time error of some kind.
To achieve this behaviour we have to set up classNotABase
So that it must do something that classes derived from it cannot do. That implies that we must make use of some property of base classes which is not inherited by Their Derived classes.
One such property is class friendship. If we madeNotABase
TheOnlyFriend of some special class (say,Uninheritable
) Then no class derived fromNotABase
Can (directly) access the private membersUninheritable
.
If we then arrange that those derived classesMustAccess some such private memberUninheritable
, The Derived classes immediately become invalid.
The most obvious way of making the derived classes access a private memberUninheritable
Is to makeUninheritable
'S constructor private, and then makeNotABase
InheritUninheritable
.NotABase
Itself can callUniheritable
'S private constructor (being a friend), but classes derived fromNotABase
Cannot directly call that Constructor (because the friendship isn't inherited ).
Such an arrangement looks something like this:
class Uninheritable
{
friend class NotABase;
private:
Uninheritable(void) {}
};
class NotABase: public Uninheritable
{
// WHATEVER
};
class NotADerived: public NotABase
{
// WHATEVER ELSE
};
Unfortunately, this does not generate the specified ed-for compile-time errors, becauseNotADerived
'S constructor only callthe (public) constructorNotABase
, Which in turn callthe (private, but friendly--NotABase
) ConstructorUninheritable
. All of which is perfectly legal.
To make this idea work (or rather, to make itNotWork), we need to arrange for the constructorNotADerived
To try and callUninheritable::Uninheritable()
Directly.
Virtual inheritance to the rescue
Fortunately, the rules of virtual inheritance (TDCS, § 12.1) require that a virtual base class's constructor isDirectlyInvoked byMost derived class. Hence, ifUninheritable
Were a virtual base classNotABase
, ThenNotADerived
Constructor wocould have to callUninheritable::Uninheritable()
Directly (rather than indirectly throughNotABase
) And the Code wocould be invalid.
This is relatively easy to arrange-we simply makeNotABase
Virtually inherit fromUninheritable
:
class Uninheritable
{
friend class NotABase;
Uninheritable(void) {}
};
class NotABase: virtual public Uninheritable
{
// WHATEVER
};
class NotADerived: public NotABase
{
// WHATEVER ELSE
};
NowNotADerived
(OrAnyOther class derived fromNotABase
!) Is obliged to callUninheritable
Constructor directly, which it can't do, not being a friend. Therefore classes derived fromNotABase
Can never compile.
GeneralizingUninheritable
The above technique works well provided we only ever want to make one class (namely,NotABase
) Uninheritable. If we want to create your such classes we need a more general solution.
What we wowould like is a mechanic that automatically creates special"Uninheritable
-Like "classes to order. These special classes wowould declare friendship to the specific classes which were to be made uninheritable, and those specific classes wowould thenBecomeUninheritable by using Ally inheriting from the Automatically-generated"Uninheritable
-Like "classes (!)
This merry-go-round of class relationships is reminiscent of the class hierarchy in "Topic 4: Self linking lists ", and the solution-"recursive" inheritance through a template-is the same:
template <class OnlyFriend>
class Uninheritable
{
friend class OnlyFriend;
Uninheritable(void) {}
};
class NotABase : virtual public Uninheritable<NotABase>
{
// WHATEVER
};
class NotADerived: public NotABase
{
// THIS CLASS GENERATES A COMPILER ERROR
};
class AnotherUninheritable
: virtual public Uninheritable<AnotherUninheritable>
{
// WHATEVER
};
// ETC.
Now, to create a classX
Which cannot be inherited from, we simply ensure thatX
(Virtually) inherits fromUninheritable<X>
.
Note that classes likeUninheritable
(AndSelfLinking
) Are known as mixins failed, because they "mix-in" some additional useful property, independent from any other behaviour of the class which inherits them.