Well, I've always heard that _public_ inheritance must be restricted to
cases of is-a. But surely there are cases when protected inheritance is
appropriate but public is not. For instance, if you are exposing a
readonly collection to your users, it may be helpful to inherit a full
featured collection privately, but make only the get accessors visible.
Sure, you could use aggregation/forwarding, but here the object is-a
readonly collection and also is-a writable collection, depending on the
user. That to me just sounds like protected inheritance. Of course, you
expose all the mutation functionality to your creator/owner by returning a
pair of pointers from a factory method, one cast to the mutable base class
and one not.
Sadly, none of this is possible in .NET.
I don't think a language should restrict a user from doing this type of
thing but I personally find it extremely ugly. IMO (and it is a religious
issue) an interface is something that the "public" deals with and they
shoulsn't see anything else. If I use a vending machine to "download" a soft
drink, I want to see the interface only - coin slot, selection buttons,
change drop, etc. I don't want to see a second interface jutting out that
has meaning to the manufacuter only (even if I can't use it). It's unsightly
and confusing. The whole thing should be hidden which is not the case with
protected or private inheritance. Your own collection example is very ugly
in practice for instance (IMHO). This is because inheritance of this type
isn't normally done to extend a base class' behaviour by adding extra
functionality or even overriding existing behaviour. It's normally done to
support a new and specifc implementation meaning your new class requires its
own independent interface. A generic collection class for instance (say a
"vector" that actually supported derivation) will have many different
functions which become off-limits the moment I create a specific collection
of type T. My new collection is designed to handle T only which usually
involves very specific behaviour. It's not a "vector" from the user's
standpoint and shouldn't be seen that way. It's a collection of T and that's
how you want your users to see it (not as a "vector" even if it's
protected/private - it still violates the "is a" relationship from their
perspective). I also may not want them invoking the base class' "erase"
function, "capacity" function, "reserve" function, etc. I can inherit from
"vector" using "protected" or "private" and only expose what I want but
users can still see it's a "vector" which is an implementation detail they
shouldn't know about (especially since its functionality is off-limits). If
I add my own "erase" function for instance (maybe even calling it "delete")
then they'll now see two "erase" functions even if only one is accessible.
The entire situation is completely unnatural and confusing notwithstanding
the inconvenenience of aggregation since it requires new wrapper functions
you get for free using inheritance (although even with inheritance you may
need to replace a non-virtual base class function which leads to hiding
issues and/or dual functions in the derived class - this can result in
confusion and difficuties understanding the class even for those maintaining
it). Nevertheless, I readily acknowledge that aggregation is a maintenance
problem since you can wind up with many wrappers which often do little more
than delegate to others (resulting in code bloat, maintenance problems,
etc.).