Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТП лекции Раздел 4.doc
Скачиваний:
16
Добавлен:
28.09.2019
Размер:
2.56 Mб
Скачать

4.8.2. Наследование данных и методов.

Гибкость языка программирования в значительной степени определяется трудо­емкостью подстройки существующих программных модулей к новым требова­ниям заказчика ПО. Степень полезности уже разработанных частей ПО значи­тельно повышается, если существует возможность, не изменяя того, что написано, добавлять к ним новые свойства, модифицировать их функционирование или убирать свойства, ставшие ненужными, наследуя при этом большую часть свойств. В языке C++, в распоряжение программиста предостав­лена целая иерархия классов и подклассов. Большинство программ пишется с использованием сотен существующих классов и тысяч методов, которые объединены в библиотеки. Появляется возможность создавать объекты существующих классов и посылать им сообщения в виде методов или создавать классы, производные от существующих, изменив в них то, что надо, и работать с объектами этих новых классов. C++ предоставляет ши­рокие возможности для создания иерархии классов и подклассов. Библиотеки C++ интенсивно разрабатываются, многие из них уже зарекомендовали себя как мощные и универсальные средства разработки ПО. Упомянем наиболее извест­ные библиотеки классов: Borland OWL (Object Windows Library) и MFC (Microsoft Foundation Classes). Для использования всей мощи какой-либо библиотеки требуются значи­тельные усилия, временные затраты и достаточно высокая квалификация пользо­вателя, владеющего технологией ООП.

Производный (Derived или Child) класс является основой для реализации меха­низма наследования свойств базового (Base или Parent) класса, а также средством подстройки класса к нуждам пользователя. Подкласс может наследовать черты нескольких базовых классов, что существен­но увеличивает гибкость и мощь механизма наследственности. Подкласс (или производный класс) наследует все данные и методы базового класса. В дополне­ние к ним он может приобрести новые данные и методы, не содержащиеся в базовом классе, но необходимые для конкретных целей, преследуемых при со­здании подкласса. Каждый объект производного класса содержит свои собствен­ные копии данных, унаследованных от базового класса. Кроме того, в нем обыч­но декларируются какие-то другие, новые данные, отсутствующие в базовом классе. Любой класс может стать базовым, если создать новый класс, объявив его производным от первого. Рассмотрим пример.

class BaseClass

{ // Компоненты базового класса

);

class SubClass: public BaseClass

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

};

Имя базового класса указывается непосредственно после имени производного, до открывающей фигурной скобки. В данном случае производный класс SubClass наследует все компоненты базового, а также содержит любые, определенные непосредственно в нем самом, компоненты.

Достаточно редко используется, но существует в языке возможность управлять атрибутами доступа унаследованных данных и методов. Обратите внимание на ключевое слово public перед именем базового клас­са, которое определяет тип наследования — в данном случае общедоступ­ный. Можно также задавать частный (private) тип наследования или защищенный (protected). Разные типы влияют на доступность компонентов базового класса в производном. Так, класс мог бы быть объявлен с описателем protected. В этом случае унаследованные из класса BaseClass public-методы перешли бы (в классе SubClass) в секцию protected. Все полученные по наследству private- и protected-данные остались бы в этих же секциях. При private-типе наследования все наследованное иму­щество в разряд private. Для работы с объектами такого класса необходимо, что­бы класс имел достаточное количество своих данных и методов, доступных извне.

Чтобы разобраться в этом вопросе, рассмотрим пример:

class BaseClass

{

public:

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

int nPublBase;

void funcBase ();

protected:

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

int nProtBase;

private:

// Частные компоненты базового, класса

int nPrivBase;

};

// class SubClass: public BaseClass // public-наследование

class SubClass: private BaseClass // private-наследование

{

public:

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

int nPublSub;

void funcSub();

protected:

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

int nProtSub;

private:

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

int nPrivSub;

};

void main(int argc, char* argv[])

{

BaseClass clBase;

SubClass clSub;

dBase.nPublBase = 7;

dBase.funcBase () ;

// clSub.nPublBase =7; // Ошибка, т. к. для класса Subclass даже

// clSub.funcBase(); // public-компоненты базового класса

// являются private-компонентами

clSub.nPublSub =77;

clSub.funcSub();

}

void BaseClass::funcBase ()

{

// Имеется доступ ко всем компонентам "своего" класса BaseClass

nPublBase = 1;

nProtBase = 2;

nPrivBase = 3;

}

void Subclass::funcSub()

{

// Имеется доступ только к public и protected компонентам

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

// являются private-компонентами и доступны только для методов класса

nPublBase = 1;

nProtBase = 2;

// nPrivBase =3; // Ошибка; здесь нет доступа к private-компонентам

// Имеется доступ ко всем компонентам "своего" класса Subclass

nPublSub = 4;

nProtSub =5;

nPrivSub = 6;

funcBase ();

}

Что интересного в этом примере? Прежде всего, обратите внимание, что компонент базового класса, наследуемый как public, сохраняет тот же тип доступа.

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

Однако часто необходимо из производного класса обеспечить доступ к некоторым компонентам базового. Чтобы не пришлось объявлять такие компоненты общедоступными (public), т. е. полностью "раскрывать" их, в языке C++ и введена специальная категория доступности — защищенная (protected). Компоненты, которые имеют такой уровень доступа, заблоки­рованы для всех частей программы, за исключением компонентов самого класса и производного от него.

Таблица. Правила наследования доступа

Тип наследования доступа

Доступность в базовом классе

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

public

public

public

protected

protected

private

недоступны

protected

public

protected

protected

protected

private

недоступны

private

public

private

protected

private

private

недоступны

По умолчанию установлен тип наследования доступа private.

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

class Subclass: BaseClass // private-наследование по умолчанию

{ public:

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

BaseClass::nPublBase; // Конкретно объявляем как public

int nPublSub;

void funcSub();

};

то из любого места программы становится возможен доступ к этому компо­ненту базового класса:

clSub.nPublBase = 7;