Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Штерн В. - Основы C++. Методы программной инженерии - 2003

.pdf
Скачиваний:
267
Добавлен:
13.08.2013
Размер:
28.32 Mб
Скачать

560 Часть III • riporpoiviiviiipoBaHiie с агрвтроваиивт и иасАВАОванивт

Объект производного класса может обращаться только к компонентам private и protected, унаследованным из базового класса. Для доступа к собственным за­ крытым компонентам, унаследованным из базового класса, следует использовать функции доступа базового класса. На первый взгляд это неразумно. Объект про­ изводного класса не имеет доступа к собственным компонентам! Ничего подобного нам раньше не встречалось.

С другой стороны, производный класс является клиентом базового класса. Базовый класс может иметь элементы (особенно данные), архитектура которых со временем меняется. Если сделать эти элементы доступными для производных классов, то их потребуется и модифицировать. При доступе через отличные от private функции производные классы защищены от влияния изменений в базо­ вых классах. По этой же причине элементы данных объявляются закрытыми, а функции-члены — общедоступными.

Листинг 13.5. Доступ к компонентам классов Base и Derived в производном объекте

для класса Derived и для клиента при наследовании в режиме public

#include <iostream> using namespace std;

class Base {

// доступ только изBase

 

private: int privB;

 

protected: int protB;

// доступ из Base и Derived

 

public: void publB()

// доступ из Base, Derived, Client

 

{ privB = 0; protB = 0; } } ;

// OKдля доступа к собственным данным

class Derived : public Base {

// режим наследования public

 

private: int privD;

 

 

protected: int protD;

 

 

public: void publD()

// OKдля доступа к собственным данным

 

{

privD = 0; protD = 0;

//

protB = 0;

// ОКдля доступа к унаследованным компонентам

privB = 0;

// нет доступа к унаследованным компонентам

 

 

} } ;

 

class Client {

// конструктор класса Client

public: ClientO

{

Derived d;

// объект производного класса

 

d.publDO;

// ОКдля доступа к сервисам public

 

d.publBO;

// ОКдля доступа к сервисам public класса Base

 

/ /

d.privD = d. protD = 0;

// нет доступа к отличным отpublic сервисам

 

/ /

d.privB=d.protB=0;

// нет доступа к отличным отpublic сервисам

 

 

};

 

int

mainO

// создает объект, выполняет программу

{

Client c;

 

return 0;

 

 

}

 

 

В листинге 13.5 показана связь между объектом производного класса и его собственными компонентами. Здесь класс Derived является производным от

класса Base в режиме public. Класс Base, как и Derived, имеет компоненты private, public и protected. Производный класс вметоде publD() может обра­

щаться ксобственным компонентам privD и protD. Кроме того, он может отно­ ситься к унаследованным компонентам public и protected класса Base — protB

и publB(). РАежду тем попытка класса Derived обратиться кзакрытым компонен­ там privB, унаследованным из класса Base, была бы синтаксической ошибкой.

Глава 13 • Подобные классы и их интерпретация

561

хотя в объекте Derived распределяется память для этого компонента. Класс Client создает объект класса Derived в своем конструкторе и обращается к обндедоступным сервисам, определенным в классе Derived (publD()) и в классе Base (publD()). Он не может обращаться к отличным от public компонентам классов Base

иDerived. Поскольку здесь демонстрируются права доступа к средствам Base

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

Наследование public — наиболее естественный режим наследования, так как при этом сохраняется связь типа "является видом" между классами. При создании производного класса в таком режиме объекты предлагают клиенту все общедо­ ступные средства, присутствующие в базовом объекте, и добавляют дополнитель­ ные. Возможности дальнейшего наследования также не ограничиваются.

Внимание при создании производного класса в режиме public унаследованные компоненты базового класса сохраняют в объектах производного класса свой статус доступа (private, protected или public). Все общедоступные (public) сервисы, определенные в производном классе и унаследованные из базового, доступны клиенту. Это самый естественный режим наследования.

Листинг 13.6 показывает более крупный пример использования наследования. Для доступа к компонентам х и у базовый класс Point предлагает два сервиса public — set() и get(). Производный класс VisiblePoint добавляет к этим средствам элемент данных visible и функции-члены show(), hide() и retrieve(). Метод showO устанавливает элемент данных visible в 1. В результате точка будет отображаться графической программой. Наследование происходит в режиме public. Вместо числовых значений для отображаемых и скрытых точек лучше было бы использовать перечисление, но для компактности примера выбраны именно числа.

Листинг 13.6. Доступ к базовым компонентам в производном объекте при наследовании в режиме public

#inclucle <iostream> using namespace std;

class Point

{

 

 

 

 

 

/ /

базовый класс

 

int

X, y;

 

 

 

 

 

 

/ /

закрытые данные базового класса

public:

 

 

 

 

 

 

 

 

 

 

void

set

(int x i ,

int

yi)

 

 

 

 

{ X = x i ; у = y i ; }

 

 

 

 

 

 

void

get

(int &xp,

int

 

&yp) const

/ /

общедоступные методы базового класса

{

xp = x;

yp = y;

}

}

;

 

 

 

 

class VisiblePoint

: public Point {

/ /

двоеточие

перед

public

int

visible;

 

 

 

 

 

 

 

 

 

public:

 

 

 

 

 

 

/ /

двоеточие

после

public

void showO

 

 

 

 

 

 

 

 

 

{ visible

= 1;

}

 

 

 

 

 

 

 

void

hideO

 

 

 

 

 

 

 

 

 

{ visible

= 0;

}

 

 

 

 

 

 

 

void

retrieve(int

&xp,

int &УР, int &vp) const;

 

 

{

xp = x;

yp = y;

 

 

 

// синтаксическая ошибка: закомментируйте ее!

get(xp,yp);

 

 

 

 

// доступ к методу базового класса

vp = visible;

}

}

;

 

// производные закрытые данные: ОК

Координаты точки: х=20 у=40 Видимость точки: visible=1

562

 

 

Часть III« Програ1^1М1ирован11е о arpempOBOHneivi и наследовониег^!

i nt mainO

 

 

 

 

{

 

 

 

 

 

 

VisiblePoint

a,b; int x,y,z;

/ /

определение двух производных методов

a.set(0,0);

b.set(20,40);

//вызов функции базового класса

a.hideO;

b.showO;

/ /

вызов метода public производного класса

a.get(x,y);

/ /

вызов функции public базового класса

Ь. retrieve(x,у,z);

/ /

вызов метода public производного класса

cout

«

"

Координаты точки: х=" « х « "

у=" « у « endl;

cout

«

"

Видимость точки: visible^" «

z «

endl;

return

0;

 

 

 

 

}

 

 

 

 

 

 

Общедоступные функции-члены базового класса Point: :set() и Point: :get() доступны в коде клиента таким же образом, как обидедоступные методы VisiblePoint. Любой объект VisiblePoint может предоставить такие возможности своим клиентам. Закрытые элементы данных Point: :х и Point: :у в классе VisiblePoint недоступны. При попытке выполнить программу появится синтаксическая ошибка в первой строке функции-члена retrieve(). Избавиться от нее можно двумя способами. Первый состоит в том, чтобы объявить элемент данных Point как protected. Если бы он был таковым в классе Point, то функция retrieveO мог­

ла бы обращаться к нему в классе VisiblePoint. Тем не менее, в клиенте он будет недоступен. Второй способ заключается в ис­ пользовании в функциях-членах VisiblePoint функций доступа Point, допускающих обращение к закрытым данным базового

Рис. 13 7 Результаты

класса. Этот способ демонстрируется во второй строке функции

программы

retrieve(). Если закомментировать первую строку retrieveO

из листинга 13.6

(^Q синтаксической ошибкой), то программа будет работать и даст

 

результаты, представленные на рис. 13.7.

Предпочтительнее использовать первый вариант, когда данные базового клас­ са объявляются protected, а не private. При этом в базовом классе применяется меньше функций доступа и упрощается исходный код производного класса. Про­ граммисты, предпочитающие использовать функции доступа, могут возразить, что прямой доступ к данным protected базового класса из производного класса представляет то же нарушение инкапсуляции, как и прямой доступ к элементам данных public при взаимодействии клиента и сервера. Как уже отмечалось выше, они правы, однако в данном случае проблема незначительна. Если вы почувствуе­ те, что могут возникнуть серьезные трудности, сделайте данные базового класса закрытыми и используйте в производных классах функции доступа. В противном случае просто не беспокойтесь об инкапсуляции больше, чем это действительно необходимо.

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

вбазовом классе, хотя они "принадлежат" объекту производного класса. Для обращения к этим элементам данных производного класса используйте функции доступа базового класса или объявите эти компоненты в базовом классе как protected. Для производного класса компоненты protected базового класса ничуть не хуже, чем компоненты public (т. е. доступны

втой же степени). Для клиента они приравниваются по доступу к private

(т. е. он не может к ним обращаться).

Глава 13 • Подобные классы и их интерпретация

563 I

Наследование в режиме protected

 

 

Такое

наследование представляет собой механизм, ограничивающий доступ

 

к сервисам базового класса. Компоненты public и protected, наследуемые из

 

базового класса, становятся компонентами protected в объекте производного

 

класса.

 

 

 

 

 

Сервисы базового класса доступны для дальнейшего наследования и исполь­

 

зуются в методах производного класса. Однако клиент не может обраш,аться

 

через объект производного класса к сервисам public базового класса, поскольку

 

 

Объект

 

теперь они являются защищенными (protected).

 

 

 

На рис. 13.8 показаны изменения в отношении

 

производного класса

 

Базовая

 

private

 

 

режима наследования protected. То, что было

 

 

 

public в базовом классе, становится protected

 

protected

 

часть объекта

 

 

в производном классе. Пунктирная линия по­

производного класса

 

protected

 

 

 

казывает, что доступ для клиента к этой части

 

 

 

Производная

 

private

 

 

объекта производного класса запрещен.

часть обг>екта

 

protected

 

Листинг 13.7 демонстрирует абстрактный

производного класса

 

public

 

Клиент

пример из листинга 13.5, когда режим насле­

Рис. 13.8. Доступ

к компонентам

базового

дования public заменяется на protected. Этот

пример иллюстрирует соотношения ме>кду объ­

класса

и производного класса

ектом производного класса и его собственными

из объекта

производного

класса

компонентами, а также между данным объек­

и из клиента при

наследовании

в режиме

protected

 

том и клиентом.

Листинг 13.7. Доступ к компонентам Base и Derived объекта Derived для класса Derived и клиента при режиме наследования protected

#include <iostream> using namespace std;

class Base {

// доступ только изBase

 

private: int privB;

 

protected: int protB;

// доступ изBase и Derived

 

public: void publB()

// доступ изBase, Derived, Client

 

{ privB = 0; protB = 0; } } ;

// OKдля доступа к собственным данным

 

class Derived : protected Base {

// режим наследования protected

 

private: int privD;

 

 

 

 

protected: int protD;

 

 

 

 

public: void publD()

// OKдля доступа

к собственным данным

 

{

privD = 0; protD = 0;

 

//

protB = 0;

// ОК для доступа

к унаследованным компонентам

privB = 0;

// нет доступа

к унаследованным компонентам

 

 

} } ;

 

 

 

 

class Client {

// код клиента

 

 

 

public:

 

 

 

 

ClientO

// объекты производного и базового классов

 

{

Derived d; Base b;

 

//

d.publDO;

// часть public производного класса: ОК

 

d.publBO;

// нет доступа к сервисам public класса Base

Derived

//

d.privD = d. protD = 0;

// нет доступа

к отличным отpublic сервисам

//

d.privB=d.protB=0:

// нет доступа

к отличным отpublic сервисам

Base

 

b.publBO; }

// объект Base: часть public, ОК

 

 

}

 

 

 

 

I 564

 

Часть III • Програмттрошаиме с агрегирование1У1 и наследованием

i nt

mainO

 

{

Client

с;

/ / создает объект, выполнят программу

 

return

0;

 

 

}

 

 

Вспомним вызов функции-члена publB() класса Base с производным объектом в качестве получателя сообщения — d.publBO (в предыдущем примере из лис­ тинга 13.5). В листинге 13.7 это синтаксическая ошибка. Заметим, что доступ к компонентам Base отвергается только тогда, когда он осуществляется через объект класса Derived. В конце применяемого по умолчанию конструктора ClientO вызываемая функция-член publB() использует в качестве целевого объекта объект b класса Base. Компонент public доступен клиентам этого класса. Данный сервис недоступен клиентам класса Derived.

Листинг 13.8. Доступ к базовым компонентам в производном объекте при наследовании в режиме protected

ttinclude

<iostream>

 

 

using

namespace std;

 

 

class

Point

{

// базовый класс

protected:

 

// закрытые данные базового класса

 

int

X,

y;

 

public:

 

 

 

 

 

void set

(int x i , int yi)

 

 

 

 

{ X = xi; у = yi; }

 

// общедоступные методы базового класса

 

void get (int &xp, int &yp) const

 

 

{ xp = x; yp = y; } } ;

 

 

class VisiblePoint : protected Point {

// наследование protected

 

int visible;

 

 

public:

 

 

 

 

 

void showO

 

 

 

{ visible = 1; }

 

 

 

void hideO

 

 

 

{ visible = 0; }

 

 

 

void retrieve(int &xp, int &УР, int &vp) const;

//

{ xp = x; yp = y;

 

// доступ к данным protected: OK

 

get(xp,yp);

 

// не будем усложнять

 

 

vp = visible; }

 

 

 

void initialize(int xp, int yp, int vp)

// новый сервис public

 

{ X = xp; У = yp;

 

// доступ к данным protected базового класса

 

 

visible = vp; } } ;

 

// доступ к данным private производного класса

int mainO

 

 

 

 

{

VisiblePoint a, b; intx.-y.z;

 

// определение двух производных методов

//

b.initialize(20,40,1);

 

// инициализация производного объекта

a.set(0,0); b.set(20,40);

 

// теперь это синтаксическая ошибка

//

a.hideO; b.showO;

 

// вызов метода public производного класса: ОК

a.get(x,y);

 

// это синтаксическая ошибка

 

b.retrieve(x,y,z);

 

// вызов метода public производного класса

cout «

" Координаты точки: x=" « x

« " у=" « У « endl;

cout «

" Видимость точки: visible="

« Z « endl;

return 0;

 

}

 

 

Глава 13 • Подобные классы и их интерпретация

565

В листинге 13.8 показан пример из листинга 13.6, где режим наследования public заменен на protected. Базовый класс Point предлагает те же сервисы set() и get(), но клиент производного класса VisiblePoint не может их использо­ вать — это защищенные объекты VisiblePoint. Попытка сделать это в конструк­ торе Client О дает синтаксическую ошибку. Чтобы разрешить проблему, в класс VisiblePoint добавлен новый сервис initializeO, обращающийся к унаследо­ ванным элементам данных х и у вместо get() и set(). Обратите внимание, что теперь производный класс без проблем обращается к базовому классу, поскольку данные базового класса объявлены как protected. В функции-члене retrieveO производного класса закомментирован вызов базовой функции get().

Если закомментировать две строки с синтаксическими ошибками, программа будет работать и давать тот же результат, что и программа из листинга 13.6 (см. рис. 13.7).

Остается надеяться, что наследование protected понравилось вам больше, чем public. Наследование public — это способ добавления сервисов в дополнение к предлагаемым базовым классам или замены некоторых сервисов (без изменения имени) на что-то более полезное для клиента производного класса. Во всех при­ мерах наследования public между производными и базовыми объектами со­ блюдается отношение "является видом". Для клиента объект SavingsAccount "является видом" объекта Account со средствами начисления процентов. Объект VisiblePoint (видимая точка) представляет собой вид объекта Point (точка) с до­ бавленным свойством "отображаемая/скрытая".

При наследовании protected все происходит иначе. Это техника для быстрого создания класса, использующего отличные от public сервисы базового класса (элементы данных х и у в листинге 13.8). Однако в этомхлучае клиентам не предоставляются сервисы public базового класса (методы set() и get() в лис­ тинге 13.8). Им предлагается другой набор сервисов (метод initializeO в лис­ тинге 13.8), которые, по тем или иным причинам, больше подходят для клиента.

В листинге 13.8 объект VisiblePoint не является объектом Point. Объекты Point предлагают клиентам методы set () и get(), а объект VisiblePoint — нет.

Еще один популярный пример использования наследования protected — со­ здание стекового класса, предоставляющего клиентам доступ к конечному элемен­ ту стека. Он является производным от класса массива, который предоставляет доступ к любому компоненту. Используя наследование, разработчик может запре­ тить клиентам работать с массивом. Стек предлагает методы push() и рор() для доступа к вершине стека.

Вначале данной главы уже отмечалось важное различие между наследованием

икомпозицией классов. Производные классы предоставляют своим клиентам все сервисы public базового класса. Составной класс не дает клиентам сервисов свои компоненты, если эти сервисы не поддерживают методы составного класса. (В приведенных примерах класс Rectangle предусматривал сервис move().)

Если нужно закрыть клиенту доступ к некоторым из существующих сервисов, не используйте наследование. Вместо него применяйте композицию классов.

В листинге 13.9 показан пример из листинга 13.8, но класс VisiblePoint те­ перь является компонентом данных класса Point, а не наследником Point в режи­ ме protected. Результат примера тот же, что и на рис. 13.7.

Сервисы set() и get() базового класса Point скрываются от клиента из-за композиции классов. Элементы данных Point оказываются скрытыми внутри объекта VisiblePoint и недоступны клиенту. В результате клиент не сможет де­ лать с объектом VisiblePoint то, что он мог делать с объектом Point — переме­ щать по экрану безотносительно к видимости. Вместо этого класс VisiblePoint предоставляет клиенту собственный интерфейс — функции-члены initializeO и retrieveO, требующие, чтобы клиент работал с видимыми объектами.

Все это применимо к любой ситуации, когда нужно спроектировать для обслу­ живания клиентов новый класс. Более того, у вас есть класс, который желательно

566 Часть III • Программирование с агрегированием и наследованием

использовать в данном проекте. Если клиенту нужны все сервисы существующего класса плюс еще что-то, лучше применять наследование из этого класса в режиме public. Для унаследованных и добавленных сервисов клиент будет использовать объекты производного класса. Если сервисы существующего класса будет исполь­ зовать не клиент, а новый класс, не стоит применять наследование, даже в режиме protected. Лучше прибегнуть к композиции классов (см. главу 12).

Л и с т и нг 13.9. Использование композиции классов вместо наследования

#include <iostream> using namespace std;

class Point

{

 

 

 

 

 

 

// компонентный класс

private:

 

 

 

 

 

 

 

 

// закрытые данные

int

X.

y;

 

 

 

 

 

 

 

public:

 

 

 

 

 

 

 

 

 

void

set

(int x i ,

int

yi)

 

 

{ X = x i ; у = y i ; }

 

 

 

// общедоступные методы базового класса

void

get (int &xp, int &yp) const

 

{

xp

= x;

yp = y;

}

}

:

 

 

class VisiblePoint

{

 

 

 

 

// нет наследования, композиция

Point pt;

 

 

 

 

 

 

 

// закрытый компонент

int

visible;

 

 

 

 

 

 

 

public:

 

 

 

 

 

 

 

 

// новый сервис для клиента

void showO

 

 

 

 

 

 

{ visible = 1; }

 

 

 

 

// новый сервис для клиента

void

hideO

 

 

 

 

 

 

 

{

visible

= 0;

}

 

 

 

 

 

void

retrieve(int

&xp,

int &yp, int &vp) const;

// замена

{

pt.get(xp,yp);

 

 

 

 

// сервисы скрыты от клиента

vp = visible;

}

 

 

 

 

 

void

i n i t i a l i z e ( i n t

xp,

int yp, int vp)

// замена

{

pt.set(xp,yp);

 

 

 

 

// сервисы скрыты от клиента

visible

= vp;

}

}

;

 

 

// аналогично скрытым частным данным

int mainO

 

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

// определение составного объекта

VisiblePoint

b; int

x,

y, z;

 

b .initialize(20,40,1);

 

 

 

// сервис составного класса

b. ShowO;

 

 

 

 

 

 

 

// сервис составного класса

b. retrieve(x,y,z);

 

 

 

у=" « у «

// сервис составного класса

cout «

" Координаты точки: x=" « x «

endl;

cout «

" Видимость точки: visible=" «

z « endl;

 

return 0;

}

Наследование в режиме protected может быть полезным в ситуации, когда разрабатывается класс (или семейство классов) для обслуживания клиентов. Советуем строить этот класс поэтапно.

Например, клиенту требуется класс D1 и нужно получить производный класс D1 из D, который, в свою очередь, является производным от класса В. Применяя на­ следование в режиме protected для создания класса D, производного от В, и клас­ са D1, производного от D, можно построить класс D1, использующий все сервисы

Глава 13 • Подобные КАОССЫ И ИХ интерпретация

567

public и protected класса D. Эти сервисы включают в себя

сервисы

public

и protected класса В. Клиент D1 не будет использовать сервисы

public классов В

иD, которые скрываются от клиента благодаря режиму наследования protected. Другими словами, наследование protected предоставляет способ для ограниче­

ния доступа клиентов к сервисам public базового класса. При этом не ограничива­ ется доступ к этим сервисам из производного класса и продолжается дальнейшее наследование.

Внимание наследование в режиме protected скрывает от клиента сервисы базового класса, используемые объектом производного класса. Тем самым искажается отношение "является видом". Если оно не важно, используйте вместо наследования protected композицию классов.

Если же чувствуете, что следует применить наследование, лучше использовать режим public. (Хотя это предвзятая точка зрения.)

Наследование в режиме private

Наследование в режиме private представляет собой технику ограничения до­ ступа к базовым сервисам не только для клиентов производных классов, но и для производных классов от данных производных классов.

Когда базовый класс используется как private, все его компоненты public и protected становятся в объекте производного класса компонентами private. Они недоступны для клиентов производных классов, а также для методов классов, производных от производного класса. Их могут использовать методы производного класса.

В этом состоит важное отличие использования базового класса как private от других режимов наследования. При наследовании protected и public правила доступа транзитивны. Если производный класс использует базовый класс для дальнейшего наследования (protected или public), то производный класс имеет те же права доступа к компонентам базового класса, что и класс, непосредственно полученный из базового. Если же наследование происходит в режиме private и производный класс используется для дальнейшего наследования, то его потомки не будут иметь доступа ни к каким компонентам базового класса. К компонентам protected и public базового класса может обращаться только класс, непосредст­ венно производный от базового (дочерний). Тем самым разработчик производного класса предотвращает использование сервисов базового класса в дальнейшем наследовании.

Как и в случае наследования protected,

 

 

private

 

общедоступный интерфейс (public) базового

Часть Base

 

 

 

private

 

класса (данные и методы) не является частью

 

 

объекта Derived

 

 

интерфейса производного класса. Для клиента

 

 

private

V\\\

все это закрыто (private).

Часть Derived

 

private

Данные связи показаны на рис. 13.9. При

 

protected

\

наследовании в режиме private объект класса,

объекта Derived

 

public

Client code

производного от производного класса, не имеет

Часть Derived

 

private

^

доступа к своим собственным компонентам,

 

 

объекта,

 

protected

 

унаследованным от базового класса.

 

 

производного

I

 

Листинг 13.10 снова показывает небольшой

от объекта Derived

public

 

абстрактный пример. На этот раз используется

 

Объект класса,

наследование private. Что же касается прав

 

производного от класса Derived,

доступа производного объекта к своей базовой

 

который является производным

части, то они здесь такие же, что и в преды­

 

от Base в режиме private

Рис. 13.9. Доступ к базовым

 

дущем примере (с наследованием в режиме

компонентам

protected).

из объекта

производного класса

 

при

наследовании

private

568

Часть III • Програ^/1мирование с агрегированием и наследованием

Листинг 13.10. Доступ к компонентам Base в иерархии наследования,

 

 

 

когда класс Derived наследует из класса Base в режиме

private

 

#include <iostream>

 

 

 

using namespace std;

 

 

 

class Base {

// доступ только из Base

 

 

 

private: int privB;

 

 

 

protected: int protB;

// доступ из Base и Derived

 

 

 

public: void publB()

// доступ из Base и Derived

 

 

 

{ privB = 0; protB = 0;} }

// OKдля доступа к собственным данным

 

class Derived : private Base {

// режим наследования private

 

 

private: int privD;

 

 

 

 

protected: int protD;

 

 

 

 

public: void publD()

// OKдля доступа к собственным данным

 

 

{ privD = 0; protD = 0;

 

//

protB = 0;

// ОКдля доступа к унаследованным компонентам

privB = 0;

// нет доступа к унаследованным компонентам

class Derivedl : public Derived {

// класс, производный отDerived

 

public: void publDDO

// нет доступа к "базовым" данным private

{

// privD = 0

protD = 0;

// OKдля доступа к "базовым" данным protected

publDO;

// OKдля доступа к "базовым" данным public

//

protB = 0;

// нет доступа клюбой части

"закрытой

базы"

 

publBO;

// нет доступа клюбой части

"закрытой

базы"

} } ;

 

 

 

class Client {

 

 

 

public:

 

 

 

ClientO

// объекты производного и базового классов

{

Derived d; Base b;

d.publDO;

// часть public класса Derived: OK

 

//

d.publBO;

// часть public Base класса Derived: не OK

//

d.privD = d. protD =

// отличная отpublic часть Derived: не OK

//

d.privB=d.protB=0;

// отличная отpublic часть Base в Derived: не ОК

b.publBO; }

// часть public Base объекта Base: OK

 

 

}

 

 

 

int mainO

// создает объект, выполняет программу

 

{

Client c;

 

return 0;

 

 

 

}

 

 

 

Чтобы программу можно было скомпилировать, здесь закомментированы не­

сколько нарушаюш,их правила строк. Производный класс может обращаться ко всем отличным от private компонентам базового класса. Это не зависит от ре­ жима наследования. Аналогично, класс Derivedl может обращаться ковсем не

закрытым компонентам собственного "базового" класса (Derived). Это также не связано с режимом наследования. Между тем в режиме private класс Derivedl не может обращаться к компонентам базового класса Base.Что касается клиента,

для него наследование private аналогично наследованию protected. Все компо­ ненты базового класса в производном объекте недоступны для клиента.

Наследование private допускает написание новых серверов с помощью уже

реализованных элементов. Однако в этом случае подтипы неимеют отношений. Если в режиме private изкласса Array создается объект Stack, последний не

является объектом Array и непредоставляет клиентам Stack или классам, на­

следующим из Stack, сервисы Array.

Глава 13 • Подобные классы и их интерпретация

569

Stack может использовать Array в качестве одного из своих элементов. При­ менение наследования private или protected — не очень хорошее решение. Ис­ пользуйте композицию классов.

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

Полиморфизм (о котором рассказывается в следуюш,ей главе) доступен только при наследовании public, и это может быть еш,е одной причиной в пользу режима наследования public. Избегайте наследования private и protected.

Изменение доступа к компонентам базового класса в производном классе

C++ позволяет разработчикам производного класса обойти многочисленные ограничения, налагаемые правилами наследования в режимах protected и private. В производном объекте компонентам базового класса возвраидаются те права доступа, которые они ранее предоставляли.

Листинг 13.11 снова показывает пример с закрытым наследованием от класса Base к Derived. В определении класса Derived восстановлен статус protected элемента данных Base: :protB. Кроме того, восстановлен статус public функциичлена Base:: publB(). Синтаксис будет одинаковым для функции-члена и элемента данных. Права доступа самого класса Derived не изменяются. При любом режиме наследования он может обращаться ко всем отличным от private компонентам базового класса. Клиент же теперь получает доступ к Base: : publB(), как если бы класс Derived наследовал от Вазе в режиме public, а не private.

Листинг 13.11. Пример изменения прав доступа к компонентам Base в классе Derived (наследование в режиме private)

#include <iostream> using namespace std;

class Base {

private: int privB; protected: int protB; public: void publB()

{ privB = 0; protB = 0; } } ;

class Derived : private Base { private: int privD; protected: int protD; protected:

Base::protB;

public:

Base::publB; public: void publD()

{ privD = 0; protD = 0; protB = 0;

//privB = 0;

}} ;

//доступ только изBase

//доступ изBase и Derived

//доступ изBase и Derived

//OKдля доступа к собственным данным

//режим наследования private

//доступно для последующего наследования //доступно для клиента

//ОК для доступа к собственным данным

//ОКдля доступа к унаследованным компонентам

//нет доступа к унаследованным компонентам

class Derivedl : public Derived { public: void publDDO

{ // privD = 0; protD = 0; publDO;

//класс, производный отDerived

//нет доступа к "базовым" данным private

//ОКдля доступа к "базовым" данным protected

//ОК для доступа к "базовым" данным public

Соседние файлы в предмете Программирование на C++