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

4.2. Конструкторы производных классов

Обратите внимание: программа с помощью класса CBlock сначала вызывает функции-члены SetCoord и SetColor для установки значений переменных класса, а затем вызывает функцию Draw для рисования блока. Чтобы упростить использование CBlock, необходимо добавить конструктор для установки значений всех переменных при создании экземпляра класса.

CBlock (int L, int T, int R, int B, int Color)

: CRectangle {L, T, R, B)

{

SetColor (Color);

}

Список инициализации членов класса в данном конструкторе содержит вызов конструктора базового класса CRectangle, которому передаются значения, присваиваемые переменным-членам. Список инициализации может использоваться для инициализации как базового класса, так и переменных членов (рассматривался в параграфе «Инициализация переменных-членов в конструкторах» гл. 3). Конструктор класса CBlock содержит вызов функции SetColor, которая устанавливает значение переменной FillColor. Теперь, используя конструктор класса, можно создать объект класса CBlock и задать блок всего двумя операторами.

CBlock Block (25, 25, 100, 100, 5};

Block.Draw ( );

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

CBlock ()

{

FillColor = 0;

}

Так как конструктор по умолчанию явно не инициализирует базовый класс (список инициализации пуст), компилятор автоматически вызывает конструктор по умолчанию базового класса (CRectangle::CRectangle ( )), присваивающий нулевые значения всем переменным, определенным внутри него. Если базовый класс не имеет конструктора по умолчанию, то будет получено сообщение об ошибке.

Конструктор по умолчанию класса CBlock позволяет создавать объекты, в которых все переменные-члены равны нулю, без передачи значений или вызова методов класса.

CBlock Block; // создается объект класса CBlock, у которого всем

// переменным-членам присвоены нулевые значения

В конце следующего параграфа приведен листинг 4.2 – полный текст класса CBlock с двумя новыми конструкторами.

Порядок вызова конструкторов и деструкторов

При создании экземпляра производного класса компилятор вызывает конструкторы в следующем порядке.

  1. Конструктор базового класса.

  2. Конструкторы всех объектов-членов (т.е. тех элементов класса, которые являются объектами классов). Эти конструкторы вызываются в порядке перечисления объектов в определении класса.

  3. Собственный конструктор класса.

Деструкторы, если они определены, вызываются в обратной последовательности.

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

4.3. Доступ к наследуемым переменным

Даже если класс CBlock наследует переменные-члены Left, Top, Right и Bottom из своего базового класса, это не означает возможности прямого доступа, так как они определены в базовом классе как закрытые. Вместо этого пришлось бы использовать открытую функцию GetCoord, как и в остальной части программы. Такое ограничение приводит к тому, что код функций-членов становится в определенной степени неэффективным. В качестве альтернативы можно указать спецификатор доступа protected вместо private (листинг 4.1).

Листинг 4.1

// CRectl.h: файл заголовков класса CRectangle

class CRectangle

{

protected:

int Left,

int Top;

int Right;

int Bottom;

public:

CRectangle ()

{

Left = Top = Right = Bottom = 0;

}

CRectangle {int L, int T, int R, int B)

{

SetCoord (L, T, R, B);

}

void Draw (void);

void GetCoord (int *L, int *T, int *R, int *B)

{

*L = Left;

*T = Top;

*R = Right;

*B = Bottom;

}

void SetCoord (int L, int T, int R, int B);

};

Напомним: в данной версии класса CRectangle предполагается определение методов Draw и SetCoord вне класса. Подобно закрытым членам, к защищенным членам класса доступ извне невозможен. Приведенный ниже способ обращения к переменной является некорректным.

void main ( )

{

CRectangle Rect;

Rect.Left =10; // ОШИБКА: доступ к защищенному

// члену класса невозможен

}

Однако в отличие от закрытых к защищенным членам класса можно непосредственно обращаться из класса, производного от того класса, в котором данная переменная определена. Следовательно, CBlock можно переписать, используя прямой доступ к переменным-членам, определенным в CRectangle (листинг 4.2).

Листинг 4.2

// CBlock.h: файл заголовков класса CBlock

#include "crectl.h"

#include <stdlib.h>

void Fill (int X, int Y, int Color);

class CBlock: public CRectangle

{

protected:

int FillColor;

public:

CBlock()

{

FillColor = 0;

}

CBlock (int L, int T, int R, int B, int Color)

: CRectangle {L, T, R, B)

{

SetColor (Color);

}

void Draw (void)

{

CRectangle::Draw ( );

Fill ((Left + Right)/2, (Top + Bottom)/2, FillColor);

}

void SetColor (int Color)

{

FillColor = _max (0, Color) ;

}

};

В данном случае переменную FillColor класса CBlock предпочтительнее определить как защищенную, а не как закрытую. Это обеспечит доступ к ней из любых классов, производных от CBlock, как описано в следующем параграфе.

Можно предоставить другому классу или глобальной функции доступ к внутренним или защищенным членам класса, объявив его или функцию дружественными (friend) для данного класса. Этот прием описан в гл. 5 «Перегрузка оператора присваивания».

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

Если класс порожден с использованием спецификатора public

class CBlock: public CRectangle

{

// определение класса ...

}

то открытые члены базового класса остаются открытыми, а защищенные – защищенными и в производном классе.

Однако если для определения производного класса использован спецификатор private

class CBlock : private CRectangle

{

// определение класса ...

}

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

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

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