Public and private inheritance in C++

2020-02-04 06:42发布

问题:

As we know from the literature for the public inheritance the object of child class (sub-class) also can be considered as the object of base class (super-class). Why the object of the sub-class can’t be considered as an object of super-class, when the inheritance is protected or private?

回答1:

Because you can't see it:

class Base
{
    public: virtual ~Base() {}
};

class PublicDerived: public Base
{    };

class PrivateDerived: private Base
{    };

int main()
{
    PublicDerived   publicD;
    PrivateDerived  privateD;

    Base&           base1 = publicD;
    Base&           base2 = privateD; // ERROR
} 

So you can not use a PrivateDerived object where a Base object could be used.
So it will never act like a Base class object.



回答2:

In general you will find in literature (and in other answers here) that protected/private inheritance imply that the class cannot be used as a base. The fact (some other answer hints into this) is that only the visibility of the inheritance is affected by the operation. The derived class is a base class, even if external code cannot see it.

Any friend or the class will be able to make use of that relationship:

struct base {
   virtual void foo() { std::cout << "base" << std::endl; }
};
void take_base( base& b ) {}
class derived : base // private {
   friend base& to_base( derived& );
   virtual void foo() { std::cout << "derived" << std::endl; }
public:
   base & as_base() { return *this; }
   void call_function() { take_base(*this); } // ok: inside derived, it knows that it is
                                              // in fact a base
};
base& to_base( derived& d ) {
   return d;
}
int main() {
   derived d;
   //d.foo();      // error
   //take_base(d); // error
   take_base( d.as_base() ); // ok, the conversion is performed internally where
                             // access is granted: print "derived"
   take_base( to_base(d) );  // ok, the conversion is performed in a friend function
                             // that has access: print "derived"
}

Now, while technically this is the case, semantically when you use private inheritance you are trying to model not an is-a but rather a implemented-in-terms-of relationship. This is the important part: while reading code, if you see private inheritance you should not think on is-a but implemented-in-terms-of.



回答3:

The "why" is simple when considering how the mechanism works: because protected and private inheritance are meant to work that way.

This is probably not enough to answer the question's intent though. You might ask "and why have private and protected inheritance if you can't use the resulting objects as instances of the base class?"

Well, non-public inheritance is meant to facilitate the "is implemented in terms of" relationship between two classes (whereas public inheritance facilitates the "is-a" relationship). In other words, you intend to reuse part or all of the base class functionality to provide services to your own consumers.

This scenario is almost always better implemented by aggregation instead of inheritance (i.e., having a member object of the "base" class), and I would go so far as to say that non-public inheritance is something better left alone.

Take a look at this for a longer write-up that expands on the above.

Update: as the commenters below state, there are some (admittedly rare) cases where non-public inheritance provides the mechanism for architectural functionality that would not otherwise be possible. Do read them, as exploring the edges of a language can be quite enlightening. But try to do it as little as you can anyway.



回答4:

In brief, because private inheritance is inheritance of implementation, not that of interface. A private subclass Derived object is not a Base, but is implemented in terms of Base. The public and protected members of Base are visible for Derived, but they become private, thus inaccessible for the outside world. Thus private inheritance can be thought of as a special form of composition, which is in fact rarely needed in practice. (And protected inheritance is practically never - in fact probably even Bjarne Stroustrup doesn't know what protected inheritance means.)



回答5:

public inheritance serves the purpose of the is-a relationship. That is:

class A {};
class B : public A {};

Class B is a version of class A.

private inheritance serves the purpose of the has-a relationship. You can write almost any class using private inheritance using a container model instead:

class A {};
class B : private A {};

can be rewritten (and more often than not, should be rewritten for clarity):

class A {};
class B
{
private:
    A a;
};

protected inheritance is similar to private, but in reality should almost never be used (Scott Meyers and Herb Sutter both give reasons for this in their respective books).



回答6:

You can think of public / protected / private inheritance like accessibility for any class member : it a matter of 'how much you want to show'.

A private (or protected, in a slightly different way) inheritance is a relationship which is not shown the outside world. As such, you can't treat an object of a derived type as its private base, because you don't get to "see" that this relationship even exists.



回答7:

Why the object of the sub-class can’t be considered as an object of super-class, when the inheritance is protected or private?

It can certainly be considered an object of the super-class. However, such consideration is restricted (by the public/protected/private inhertiance modifier) to but only by itself (private inheritance) or it's sub-classes (protected inheritance).

All external objects are not allowed to considered the class as such, similar to how they not allowed to access protected or private methods or variables. The analogy is rather fitting, if expressed properly.

So, the class itself, its subclasses (and friends) can see this as an is-a relationship, but the outside world is not permitted to do so.

The following code shows this in action:

class Base {
    public: virtual ~Base() {}
};

class PublicDerived: public Base
{ };

class ProtectedDerived: protected Base {
    void test() {
        Base* base2 = this; // OK
    }
};

class ProtectedSubClass: public ProtectedDerived {
    void test() {
        Base* base2 = this; // OK
    }
};

class PrivateDerived: private Base {
    void test() {
        Base* base2 = this; // OK
    }
};

class PrivateSubClass: public PrivateDerived {
    void test() {
        Base* base2 = this; // Error (line 28)
    }
};

int main()
{
    PublicDerived   publicD;
    ProtectedDerived protectedD;
    PrivateDerived  privateD;

    Base* base1 = &publicD;
    Base* base2 = &protectedD; // Error (line 39)
    Base* base3 = &privateD; // Error (line 40)
} 

Note that it doesn't matter how the xxxSubClass-classes derive from their super-classes. It's all about how the super-classes derive from Base, which is as it should be.

The compiler complains appropriately:

inherit.cpp(28) : error C2247: 'Base' not accessible because 'PrivateDerived' uses 'private' to inherit from 'Base'
        inherit.cpp(1) : see declaration of 'Base'
        inherit.cpp(20) : see declaration of 'PrivateDerived'
        inherit.cpp(1) : see declaration of 'Base'
inherit.cpp(29) : error C2243: 'type cast' : conversion from 'PrivateSubClass *const ' to 'Base *' exists, but is inaccessible
inherit.cpp(39) : error C2243: 'type cast' : conversion from 'ProtectedDerived *' to 'Base *' exists, but is inaccessible
inherit.cpp(40) : error C2243: 'type cast' : conversion from 'PrivateDerived *' to 'Base *' exists, but is inaccessible