logo
Объектно-ориентированное программирование и C++

3.4.3 Объявление производных классов

C++Builder дает возможность объявить производный класс, который наследует свойства, данные, методы и события всех своих предшественников в иерархии классов, а также может объявлять новые характеристики и перегружать некоторые из наследуемых функций. Наследуя указанные характеристики базового класса, можно заставить порожденный класс расширить, сузить, изменить, уничтожить или оставить их без изменений.

Наследование позволяет повторно использовать код базового класса в экземплярах производного класса. Концепция повторного использования имеет параллель в живой природе: ДНК можно рассматривать как базовый материал, который каждое порожденное существо повторно использует для воспроизведения своего собственного вида. <

Листинг 3.5 иллюстрирует обобщенный синтаксис объявления производного класса. Порядок перечисления секций соответствует расширений привилегий защиты и областей видимости заключенных в них элементов: от наиболее ограниченных к самым доступным.

class className : [^спецификатор доступа;”] parentClass {

<0бъявления дружественных классов>

private:

<приватные члены данных>

<приватные конструкторы>

<приватные методы> protected:

<защищенные члены данных>

<защищенные конструкторы>

<защищенные методы> public:

<общедоступные свойства>

<общедоступные члены данных>

<общедоступные конструкторы>

<общедоступный деструктор>

<общедоступные методы> _published:

•<общеизвестные свойства>

<общеизвестные члены данных>

<Объявления дружественных функций>

Листинг 3.5. Объявление производного класса.

Отметим появление новой секции с ключевым словом _published - дополнение, которое C++Builder вводит в стандарт ANSI C++ для объявления общеизвестных элементов компонентных классов. Эта секция отличается от общедоступной только тем, что компилятор генерирует информацию RTTI о свойствах, членах данных и методах объекта и C++Builder организует передачу этой информации Инспектору объектов во время исполнения программы. В главе 6 мы остановимся на этом более подробно.

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

Когда класс порождается от базового, все его имена в производном классе автоматически становятся приватными по умолчанию. Но его легко изменить, указав следующие спецификаторы доступа базового класса:

• private. Наследуемые (т.е. защищенные и общедоступные) имена базового класса становятся недоступными в экземплярах производного класса.

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

Можно порождать классы, которые расширяют возможности базового класса:

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

Рассмотрим применение методик расширения и ограничения характеристик на примере создания разновидностей объекта кнопки - типичных производных классов, получаемых при наследовании базовой компоненты TButtonControl из Библиотеки Визуальных Компонент C++Builder. Кнопки различного вида будут часто появляться в диалоговых окнах графического интерфейса ваших программ.

Рис. 3.1 показывает, что базовый класс TButtonControl способен с помощью родительского метода Draw отображать кнопку в виде двух вложенных прямоугольников: внешней рамки и внутренней закрашенной области.

Чтобы создать простую кнопку без рамки (Рис. 3.2), нужно построить производный класс SimpleButton, использовав в качестве родительского TButtonControl, и перегрузить метод Draw с ограничением его функциональности (Листинг 3.6)

class SimpleButton: public : TButtonControl { public:

SimpleButton(int x, int y) ;

void Draw() ;

-SimpleButton() { }

};

SimpleButton::SimpleButton(int x, int y) :

TButtonControl(x, y) { }

void SimpleButton::Draw()

{ ; outline->Draw();

}

Листинг 3.6. Ограничение характеристик базового класса.

Единственная задача конструктора объекта для SimpleButton - вызвать базовый класс с двумя параметрами. Именно переопределение метода SimpleButton: : Draw () предотвращает вывод обводящей рамки кнопки (как происходит в родительском классе). Естественно, чтобы изменить код метода, надо изучить его по исходному тексту базовой компоненты TButtonControl.

Теперь создадим кнопку с пояснительным названием (Рис. 3.3). Для этого нужно построить производный класс TextButton из базового TButtonControl, и перегрузить метод Draw с рас-Рис. 3.3. Кнопка с текстом, ширением его функциональности.

Листинг 3.7 показывает, что объект названия title класса Text создается конструктором TextButton, а метод

SimpleButton:-.Draw () отображает его. :

class Text { public:

Text(int x, int y, char* string) { } void Draw() { } };

class TextButton: public : TButtonControl {

Text* title;

public:

TextButton(int x, int y, char* title);

void Draw();

-TextButton() { } );

TextButton::TextButton(int x, int y, char* caption)

TButtonControl(x, y) {

title = new Text(x, y, caption);

}

void TextButton::Draw () {

TextButton::Draw() ;

title->Draw() ;

}

Листинг 3.7. Расширение характеристик базового класса.

В заключение раздела с изложением методики разработки базовых и производных классов приводится фрагмент C++ программы (Листинг 3.8), в которой объявлена иерархия классов двух простых геометрических объектов: окружности и цилиндра.

Программа составлена так, чтобы внутренние значения переменных г-радиус окружности и h-высота цилиндра определяли параметры создаваемых объектов. Базовый класс Circle моделирует окружность, а производный класс Cylinder моделирует цилиндр.

class SimpleButton: public : TButtonControl { public:

SimpleButton (int x, int y) ;

void Draw() ;

-SimpleButton() { } );

SimpleButton::SimpleButton(int x, int y) :

TButtonControl(x, y) { }

I void SimpleButton::Draw()

I { i outline->Draw();

1 )

Листинг 3.6. Ограничение характеристик базового класса.

Единственная задача конструктора объекта для SimpleButton - вызвать базовый класс с двумя параметрами. Именно переопределение метода SimpleButton: : Draw () предотвращает вывод обводящей рамки кнопки (как происходит в родительском классе). Естественно, чтобы изменить код метода, надо изучить его по исходному тексту базовой компоненты TButtonControl.

Теперь создадим кнопку с пояснительным названием (Рис. 3.3). Для этого нужно построить производный класс TextButton из базового TButtonControl, и перегрузить метод Draw с рас-Рис. 3.3. Кнопка с текстом, ширением его функциональности.

Листинг 3.7 показывает, что объект названия title класса Text создается конструктором TextButton, а метод

SimpleButton: : Draw () отображает его.

const double pi = 4 * atan(l);

class Circle { protected:

double r ;

public:

Circle (double rVal =0) : r(rVal) {}

void setRadius(double rVal) { r = rVal; }

double getRadiusO { return r; } .double Area() { return pi*r*r; }

void showData() ;

};

class Cylinder : public Circle { protected:

double h;

public:

Cylinder(double hVal = 0, double rVal = 0)

: getHeight(hVal), Circle(rVal) { }

void setHeight(double hVal) { h = hVal; }

double getHeight() { return h; }

double Area() { return 2*Circle::Area()+2*pi*r*h; }

void showData() ;

void Circle::showData() {

cout “ "Радиус окружности = " “ getRadius() “ endl

“ "Площадь круга = " “ Area О “ endl “ endl;

}

void Cylinder::showData()

{

cout “ "Радиус основания = " “ getRadius() “ endl

“ "Высота цилиндра = " “ getHeight() “ endl

“ "Площадь поверхности = " “ Area () “ endl;

}

void main()

{

Circle circle(2) ;

Cylinder cylinder(10, 1);

circle.showData () ;

cylinder.showData() ;

Листинг 3.8. Простая иерархия классов окружности и цилиндра.

Объявление класса Circle содержит единственный член данных r, конструктор и ряд методов. При создании объекта конструктор инициализирует член данных r начальным значением радиуса окружности. Отметим новый синтаксис конструктора: при вызове он может обратиться к конструктору базового класса, а также к любому члену данных, указанному после двоеточия. В нашем случае член данных r "создается" обращением к нему с параметром rVal и инициализируется нулевым значением.

Метод setRadius устанавливает, a getRadius возвращает значение члена данных г. Метод Area возвращает площадь круга. Метод showData выдает значения радиуса окружности и площади круга.

Класс Cylinder, объявленный как производный от Circle, содержит единственный член данных h, конструктор и ряд методов. Этот класс наследует член данных г для хранения радиуса основания цилиндра и методы setRadius и getRadius. При создании объекта конструктор инициализирует члены данных г и h начальными значениями. Отметим новый синтаксис конструктора: в нашем случае член данных h инициализируется значением аргумента hVal, а член данных г - вызовом конструктора базового класса Circle с аргументом rVal.

Функция setHeight устанавливает, a getHeight возвращает значение члена данных h. Circle::Area перегружает унаследованную функцию базового класса, чтобы теперь возвращать площадь поверхности цилиндра. Функция showData выдает значения радиуса основания, высоты и площади поверхности цилиндра.

Функция main создает окружность circle класса Circle с радиусом 2 и цилиндр cylinder класса Cylinder с высотой 10 и радиусом основания 1, а затем обращается к showData для вывода параметров созданных объектов:

Радиус окружности = 2 Площадь круга = 12.566

Радиус основания = 1 Высота цилиндра = 10 Площадь поверхности = 69.115