Публичное и частное наследование в C++

Как мы знаем из литературы для публичного наследования, объект дочернего класса (подкласса) также можно рассматривать как объект базового класса (суперкласса). Почему объект подкласса нельзя рассматривать как объект суперкласса, когда наследование защищено или закрыто?

7 ответов


потому что вы не можете видеть это:

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
} 

таким образом, Вы не можете использовать объект PrivateDerived, где может использоваться базовый объект.
Поэтому он никогда не будет действовать как объект базового класса.


в общем, вы найдете в литературе (и в другие ответы здесь)protected/private наследование означает, что класс не может использоваться как base. Факт (некоторые другие ответы намекают на это) заключается в том, что только видимость наследования зависит от операции. The derived класс и a base класс, даже если внешний код не может быть!--12-->посмотреть его.

любой friend или класс сможет использовать это отношения:

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"
}

теперь, хотя технически это так, семантически, когда вы используете private наследование вы пытаетесь моделировать не is-a, а implemented-in-terms-of отношения. Это важная часть: при чтении кода, если вы видите private наследования вы не должны думать о is-a но реализовано-в-плане-о.


"Почему" просто при рассмотрении того, как работает механизм: потому что защищенное и частное наследование предназначены для работы таким образом.

этого, вероятно,недостаточно, чтобы ответить на намерение вопроса. Вы можете спросить: "А почему?"--3-->есть частное и защищенное наследование, если вы не можете использовать результирующие объекты в качестве экземпляров базового класса?"

Ну, непубличное наследование призвано облегчить" реализуется с точки зрения " отношений между двумя классами (в то время как публичное наследование облегчает отношения "is-a"). Другими словами, вы собираетесь использовать часть или все функциональные возможности базового класса для предоставления услуг собственных потребителей.

этот сценарий почти всегда лучше реализуется агрегацией вместо наследования (т. е. имея объект-член "базового" класса), и я бы сказал, что непубличное наследование-это то, что лучше оставить в покое.

взгляните на этой для более длинной записи, которая расширяется выше.

обновление: как отмечают комментаторы ниже, есть некоторые (по общему признанию, редкие) случаи, когда непубличное наследование обеспечивает механизм архитектурной функциональности, который в противном случае был бы невозможен. Прочтите их, так как изучение краев языка может быть весьма поучительным. Но постарайся делать это как можно меньше.


вкратце, потому что приватное наследование-это наследование реализация, а не интерфейс. Частный подкласс Derived объект - это не Base, а реализуется в терминах Base. Открытые и защищенные члены Base видит Derived, но они становятся частная, таким образом, недоступной для внешнего мира. Таким образом, частное наследование можно рассматривать как особую форму состав, что на самом деле редко требуется на практике. (И защищенное наследование практически никогда - на самом деле, вероятно, даже Бьярне Страуструп не знает, что означает защищенное наследование.)


public наследование служит цели is-отношения. То есть:

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

Class B is a version of class A.

private наследование служит цели отношений has-a. Вы можете написать почти любой класс, используя частное наследование, используя модель контейнера:

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

может быть переписан (и чаще всего, должны быть переписаны для ясности):

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

protected наследование похоже на private, но на самом деле почти никогда не следует использовать (Скотт Мейерс и Херб Саттер оба приводят причины этого в своих соответствующих книгах).


вы можете думать о публичных / защищенных / частная наследство, как доступность для любого члена класса : это вопрос 'сколько вы хотите показать.

частное (или защищенное, немного по-другому) наследование-это отношения, которые не показаны внешнему миру. Таким образом, вы не можете рассматривать объект производного типа как его частную базу, потому что вы даже не "видите", что эта связь существует.


почему объект подкласса не может рассматриваться как объект супер-класс, когда наследование охраняемый или частный?

это, безусловно, можно считать объектом супер-класса. однако такое рассмотрение ограничено (модификатором public/protected/private inhertiance), но только (закрытое наследование) или это суб-классы (защищенное наследование).

все внешние объекты не допускаются к рассмотрению класса как такового,подобно тому, как им не разрешен доступ к защищенным или частным методам или переменным. Аналогия вполне уместна, если выражена правильно.

Итак, сам класс, его подклассы (и друзья) могут видеть это как отношения "есть-есть", но внешнему миру это не разрешено.

следующий код показывает это в действии:

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)
} 

обратите внимание, что не имеет значения, как xxxSubClass-классы наследуют от своих супер-классы. это все о том, как супер-классы выводятся из Base, как и должно быть.

компилятор правильно ругается:

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