Публичное и частное наследование в C ++ - PullRequest
12 голосов
/ 06 января 2011

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

Ответы [ 7 ]

21 голосов
/ 07 января 2011

Потому что вы этого не видите:

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, где может использоваться объект Base.
Поэтому он никогда не будет действовать как объект базового класса.

7 голосов
/ 07 января 2011

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

Любой 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 , а реализовано в терминах .

6 голосов
/ 07 января 2011

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

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

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

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

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

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

5 голосов
/ 07 января 2011

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

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, но на самом деле почти никогда не должно использоваться (Скотт Мейерс и Херб Саттер оба приводят причины для этого в своих соответствующих книгах).

5 голосов
/ 07 января 2011

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

3 голосов
/ 07 января 2011

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

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

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

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

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

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
3 голосов
/ 07 января 2011

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

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

...