называемые содержит (has-a). В окружающем мире есть множество примеров закрытого наследования (табл. 10.2).
ТАБЛИЦА 10.2. Примеры закрытого наследования из повседневной жизни
Базовый класс
Примеры производных классов
Motor (Мотор)
Heart (Сердце)
Refill (Стержень)
Moon (Луна)
Саг (Автомобиль содержит мотор)
Mammal (Млекопитающее содержит сердце)
Реп (Ручка содержит стержень)
Sky (Небо содержит луну)
Давайте рассмотрим закрытое наследование на примере отношений автомобиля с его мотором (листинг 10.8).
ЛИСТИНГ 10.8. Класс Саг, связанный с классом Motor через закрытое наследование
#include
using namespace std;
class Motor
{
public:
void Switchlgnition()
{
8 cout « "Ignition ON" « endl;
}
void PumpFuelO
{
cout « "Fuel in cylinders" « endl;
}
void FireCylinders()
{
cout « "Vroooom" « endl;
}
};
19
class Car:private Motor
{
public:
void Move ()
{
Switchlgnition();
PumpFuel();
FireCylinders();
}
};
30
int main()
{
Car myDreamCar;
myDreamCar.Move();
return 0;
}
Защищенное наследование
|
267
|
Результат
Ignition ON
Fuel in cylinders
Vroooom
Анализ
Класс M otor, определенный в строках 3-18, очень прост, он содержит три защищен ные функции-члена, включая зажигание ( S w i t c h l g n i t i o n O ) , подачу топлива (Pump F uel ()) и запуск ( F ir e C y lin d e r s ()). Класс С аг наследует класс M otor с использовани ем ключевого слова p r i v a t e (строка 20). Таким образом, открытая функция C a r : :Move () обращается к членам базового класса M otor. Если вы попытаетесь вставить в функцию
a in ( ) строку myDreamCar.PumpFuel();
то получите при компиляции ошибку с сообщением e r r o r С 2 247 : M o to r : : Pum pFuel
T ct a c c e s s i b l e b e c a u s e C2247: M o to r : : Pum pFuel следовании от ' M o to r ').
’C a r ’ u s e s 'p r i v a t e '
недоступен, поскольку
to i n h e r i t fro m 'M o to r '
'C a r ' использует 'p r i v a t e '
(ошибка при на
ПРИМЕЧАНИЕ
Если от класса Саг произойдет другой класс, например SuperCar, то, неза висимо от характера наследования, у класса SuperCar не будет доступа к от
крытым членам и методам базового класса Motor. Дело в том, что отношения наследования между классами Саг и Motor имеют закрытый характер, а зна чит, доступ для всех остальных, кроме класса Саг, будет закрытым (т.е. доступа не будет), даже к открытым членам базового класса.
Другими словами, наиболее ограничивающий модификатор доступа доминиру ет при принятии компилятором решения о том, должен ли у некого класса быть доступ к открытым или защищенным членам базового класса.
Защищенное наследование
Защищенное наследование отличается от открытого наличием ключевого слова p r o je c te d в строке объявления производного класс как происходящего от базового класса:
class Base
{
// ... переменные-члены и методы базового класса
};
class Derived: protected Base // защищенное наследование
{
// ... переменные-члены и методы производного класса
};
Защищенное наследование подобно закрытому в следующем:
■ Реализует отношения содержит (has-a).
268 ЗАНЯТИЕ 10. Реализация наследования
Позволяет производному классу обращаться ко всем открытым и защищенным членам базового.
Вне иерархии наследования нельзя при помощи экземпляра производного класса об ратиться к открытым членам базового класса.
Но защищенное наследование все же отличается от закрытого, когда дело доходит до следующего производного класса, унаследованного от него:
class Derived2: protected Derived
{
// имеет доступ к членам Base
};
Иерархия защ ищ енного наследования позволяет производному классу производно го класса (т.е. классу D e riv e d 2 ) обращаться к открытым членам базового класса (ли стинг 10.9). Это не было бы возможно, если бы при наследовании классом D e riv e d класса B ase использовалось ключевое слово p r i v a t e .
ЛИСТИНГ 10.9. Класс SuperCar, производный от класса Саг,
происходящего от класса Motor, при защищенном наследовании________________________
#include
using namespace std;
2 :
class Motor
{
public:
void Switchlgnition()
{
cout « "Ignition ON" « endl;
}
void PumpFuelO
11: {
cout « "Fuel in cylinders" « endl;
}
void FireCylinders()
{
cout « "Vroooom" « endl;
}
};
19:
class Car:protected Motor
21: {
public:
void Move()
{
Switchlgnition();
PumpFuelO;
FireCylindersО ;
}
};
30:
class SuperCar:protected Car
{
public:
void Move()
Защищенное наследование
|
269
|
{
Switchlgnition(); // имеет доступ к членам базового благодаря
27: PumpFuelO; // защищенному наследованию между Саг и Motor
FireCylinders();
FireCylinders();
4 9:FireCylinders ();
41: }
42: };
43:
44: int main()
45: {
SuperCar myDreamCar;
myDreamCar.Move();
return 0;
- 3: }
Результат
Ignition ON
Fuel in cylinders
Vroooom
Vroooom
Vroooom
Анализ
Класс C a r защ ищ енно наследует класс M o to r (строка 20). Класс S u p e r C a r за щищенно наследует класс С аг (строка 31). Как можно заметить, реализация метода
S u p e rC a r: :Move () использует открытые методы, определенные в базовом классе Мо-7or. Этот доступ к самому первому базовому классу M otor через промежуточный базо вый класс Саг обеспечивают отношения между классами Саг и M otor. Если бы это было закрытое наследование, а не защищенное, то у производного класса не было бы доступа
открытым членам M o to r, поскольку компилятор выберет самый ограничивающ ий из использованных модификаторов доступа. Обратите внимание, что характер отношений между классами Саг и S u p e rC a r не имеет значения при доступе к базовому классу. Так,
даже если в строке 31 заменить ключевое слово p r o t e c t e d на p u b l i c или p r i v a t e , ис ход компиляции этой программы остается неизменным.
ВНИМАНИЕ!
Используйте закрытое или защищенное наследование только по мере необхо димости.
В большинстве случаев, когда используется закрытое наследование (как у классов Саг и Motor), базовый класс также может быть атрибутом (членом) класса Саг, а не суперклассом. При наследовании от класса Motor вы, по существу, огра ничили свой класс Саг наличием только одного мотора, без какого-либо суще ственного выигрыша от наличия экземпляра класса Motor как закрытого члена.
Автомобили развиваются, и сейчас не редкость гибридные автомобили, напри мер, в дополнение к обычному мотору может применяться газовый или элек трический. Наша иерархия наследования для класса Саг оказалась бы узким местом, попытайся мы последовать за такими разработками.
270 ЗАНЯТИЕ 10. Реализация наследования
ПРИМЕЧАНИЕ
Наличие экземпляра класса Motor как закрытого члена, вместо наследования от него, называется композиция (composition) или объединение (aggregation). Такой класс Саг выглядел бы следующим образом:
class Саг
{
private:
Motor heartOfCar;
public:
void Move()
{
heartOfCar.Switchlgnition ();
heartOfCar.PumpFuel();
heartOfCar.FireCylinders();
}
};
Это может быть хорошим ходом, поскольку позволяет легко добавлять к суще ствующему классу Саг больше моторов как атрибутов, не изменяя его иерар хию наследования или предоставляемые клиентам возможности.
Проблема отсечения
Что будет, если программист сделает следующее?
Derived objectDerived;
Base objectBase = objectDerived;
Или следующее?
void FuncUseBase(Base input);
Derived objectDerived;
FuncUseBase(objectDerived); // objectDerived будет отсечен при
Do'stlaringiz bilan baham: |