
- •Раздел 4. Разработка по Тема 4.1. Проектирование интерфейса с пользователем
- •4.1.1. Типы пользовательских интерфейсов.
- •4.1.2. Пользовательская и программная модели интерфейса.
- •4.1.3. Разработка диалогов.
- •4.1.4. Основные компоненты графических пользовательских интерфейсов.
- •Тема 4.2. Реализация графических пользовательских интерфейсов.
- •4.2.1. Диалоги, управляемые пользователем.
- •4.2.2. Диалоги, управляемые системой.
- •4.2.3. Использование метафор.
- •4.2.4. Технология Drag and Drop.
- •4.2.5. Интеллектуальные элементы.
- •4.3.1. Базовые типы данных.
- •Константы
- •Область действия имен
- •4.3.2. Указатели и адресная арифметика.
- •4.3.3. Составные типы данных. Структуры
- •Битовые поля
- •Определение типов
- •Перечислимые типы
- •4.3.4. Выражения и операции.
- •4.3.5. Управляющие конструкции. Условные операторы
- •Операторы циклов
- •4.4.1. Статические одномерные массивы.
- •4.4.2. Статические многомерные массивы.
- •4.4.3. Динамические массивы.
- •4.4.4. Массивы указателей.
- •4.5.1. Стеки.
- •4.5.2. Очереди.
- •4.5.3. Списки.
- •4.5.4. Бинарные деревья.
- •4.6.1. Объявление классов и экземпляров классов.
- •4.6.2. Инкапсуляция данных и методов.
- •4.6.3. Конструкторы классов.
- •Конструктор по умолчанию
- •Конструктор копирования
- •4.6.4. Деструкторы классов.
- •4.7.1. Разделы в описании класса.
- •4.7.2. Friend-конструкции.
- •4.7.3. Статические члены классов.
- •4.7.4. Использование описателя const в классах.
- •4.8.1. Вложенность классов.
- •4.8.2. Наследование данных и методов.
- •4.8.3. Типы наследования.
- •4.9.1. Полиморфизм раннего связывания.
- •4.9.2. Полиморфизм позднего связывания и виртуальные функции.
- •4.9.3. Абстрактные методы и классы.
- •4.10.1. Функции консольного ввода-вывода.
- •4.10.2. Функции файлового ввода-вывода.
- •4.10.3. Использование библиотеки классов потокового ввода-вывода.
- •4.11.1. Перегрузка операций.
- •4.11.2. Шаблоны функций.
- •4.11.3. Шаблоны классов.
- •4.11.4. Обработка исключений.
- •Тема 4.12. Com-технология.
- •4.12.1. Основные понятия.
- •4.12.2. Типы интерфейсов.
- •Свойства интерфейсов
- •Типы интерфейсов
- •4.12.3. Типы com-объектов.
- •4.12.4. Фабрика классов.
- •Тема 4.13. Построение com-сервера.
- •4.13.1. Язык idl.
- •Содержимое файла idl
- •4.13.2. Определение пользовательского интерфейса.
- •4.13.3. Реализация пользовательского интерфейса.
- •4.13.4. Создание тестового клиента.
- •Тема 4.14. Обзор платформы ms .Net.
- •4.14.1. Общая идея архитектуры .Net.
- •4.14.2. Достоинства и недостатки .Net.
- •4.14.3. Схема трансляции программ в .Net.
- •4.14.4. Язык msil.
- •4.14.5. Объектно-ориентированная модель .Net.
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;