Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по С++ глава 4.doc
Скачиваний:
4
Добавлен:
05.11.2018
Размер:
131.07 Кб
Скачать

4. Производные классы C++

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

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

4.1. Производные классы

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

Если создавать СВlock как полностью новый класс, придется продублировать большую часть всего написанного ранее для CRectangle. К счастью, средства языка C++ позволяют избежать дублирования кода и данных благодаря возможности создавать новые классы как производные от существующих. Создаваемый производный класс наследует все данные и функции, принадлежащие существующему классу.

Например, CBlock можно определить следующим образом.

class CBlock : public CRectangle

{

};

Выражение: public CRectangle обозначает класс CBlock как производный от CRectangle. Класс CBlock наследует все переменные-члены и функции CRectangle. Другими словами, несмотря на то, что определение класса CBlock является пустым, он уже содержит функции GetCoord, SetCoord и Draw, а также переменные Left, Top, Right и Bottom, определенные в классе CRectangle. При этом CRectangle называют базовым, а класс CBlock — производным классами.

Совет

В общем случае спецификатор public следует включать в первую строку определения производного класса, как показано в приведенном примере. Использование данного спецификатора приводит к тому, что все открытые члены базового класса (объявленные как public), остаются открытыми в производном классе.

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

class CBlock : public CRectangle

{

private:

int FillColor; //хранит цвет, используемый для закрашивания блока public:

void Draw (void)

{

int L, T, R, B;

CRectangle::Draw ();

GetCoord (&L, fiT, &R, &B) ;

Fill ((L + R) /2, (T + B) /2, FillColor);

}

void SetColor (int Color)

{

FillColor = __max (0, Color);

}

};

Кроме наследуемых членов, CBlock содержит закрытую переменную FillColor для хранения цветового кода заливки блока. Значение переменной FillColor устанавливается с помощью новой открытой функции-члена SetColor, Класс CBlock также содержит новую версию функции Draw для рисования прозрачных прямоугольников с последующей заливкой сплошным цветом.

Фактически класс CBlock включает две версии функции Draw — наследуемую и определенную явно. При вызове функции Draw для объекта типа CBlock версия функции, определенная внутри CBlock, переопределяет функцию, определенную внутри CRectangle, как показано в следующем фрагменте программы.

CBlock Block;

// ...

Вlock.Draw (); // вызов версии функции Draw, определенной внутри

// класса CBlock, так как объект Block имеет тип CBlock

Примечание

Далее в параграфе "Применение виртуальных функций" рассмотрен более эффективный способ задания и переопределения функций класса.

Версия функции Draw, определенная для CBlock, начинается с вызова версии функции Draw класса CRectangle, рисующей прозрачный прямоугольник.

CRectangle::Draw();

Использование выражения с оператором расширения области видимости (CRectangle::) приводит к вызову версии функции Draw, принадлежащей классу CRectangle. Если же данное выражение будет опущено, то компилятор сгенерирует рекурсивный вызов функции Draw, определенной для текущего класса CBlock. Это пример того, как функция, определенная в рамках текущего класса, заменяет собой наследуемую функцию.

На следующем этапе Draw вызывает наследуемую функцию GetCoord для вычисления текущих значений координат прямоугольника, а затем – функцию Fill для заливки внутренней области прямоугольника цветом, соответствующим текущему цветовому коду.

GetCoord (&L, &Т, &R, &В);

Fill ((L + R) /2, (Т + В) / 2, FillColor);

Fill – это гипотетическая функция заливки замкнутой области. Первые два параметра задают координаты точки внутренней области, а третий — цветовой код, используемый при заливке этой области. Такая функция может быть определена в любом месте программы или вызвана из библиотеки функций.

Приведем фрагмент программы, в котором используется класс CBlock.

CBlock Block; // создание экземпляра класса CBlock

Block.SetCoord (25,25,100,100); // установка координат блока

Block.SetColor (5); // установка цвета блока

Block.Draw (); // вызов функции Draw класса CBlock