Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Любой 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" ). Другими словами, вы намерены повторно использовать часть или все функциональные возможности базового класса для предоставления услуг своим собственным потребителям.

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

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

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

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

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

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

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

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