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
- 3.1 Инкапсуляция
- 3.2 Классы, компоненты и объекты
- 3.3 Наследование
- 3.4 Разработка классов
- 3.4.1 Объявление базового класса
- 3.4.3 Объявление производных классов
- 3.5 Полиморфизм
- 3.5.1 Виртуальные функции
- 3.5.2 Дружественные функции
- 3.6.1 Компоненты
- 3.6.1.1 Объявления компонентных классов
- 3.6.1.3 Объявления обработчиков событий '
- 3.6.1.4 Объявления автоматизированных свойств и методов
- 3.6.1.6 Расширенные типы данных Delphi
- Variant Вариантное значение, 16 байт Variant class
- 3.6.2.2 Пространства имен
- 3.6.2.3 Явные объявления
- 3.6.2.6 Исключения
- 3.7 Итоги