- •Раздел 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.3. Типы наследования.
Производный класс сам может служить базовым, т. е. от него можно образовывать новые классы. В этом случае полученный класс включит в себя элементы всех его базовых классов. От одного базового класса можно образовывать несколько производных. Таким образом, возможна древовидная структура иерархии классов.
class Basel
{
// Компоненты первого базового класса
};
class Base2
{
// Компоненты второго базового класса
}; ,
class Derived: public Basel, private Base2
{
// Компоненты производного класса
};
Во всех рассмотренных примерах производный класс был образован от одного базового класса — так называемое единичное наследование. Однако механизм наследования в C++ более развит и включает наряду с единичным и множественное наследование, при котором производный класс образуется сразу от нескольких базовых, что позволяет создавать достаточно сложные иерархии классов (Графовая иерархия классов).
В объявлении класса может быть записано любое количество базовых классов, перечисленных через запятую. Порядок, в котором перечисляются базовые классы, произвольный. Он влияет только на последовательность вызова конструкторов, которая является следующей:
Сначала вызываются конструкторы всех виртуальных базовых классов; если их несколько, то конструкторы вызываются в порядке их наследования.
Затем конструкторы не виртуальных базовых классов в порядке их наследования.
И, наконец, конструкторы всех компонентных классов.
Как обычно, последовательность вызова деструкторов является обратной относительно последовательности вызовов конструкторов.
При множественном наследовании атрибуты доступа (прав пользования) указываются отдельно для каждого родительского класса. Например:
class А : private В. protected С. D, public E {...};
Все данные и методы класса В наследуются классом А с правами private, члены класса С наследуются им же с правами protected, члены класса D наследуются с правами private (по умолчанию), а члены класса Е — с правами public. Таким образом, protected- и public-члены класса С попадают в секцию protected класса А. Все члены классов В и D попадают в секцию private класса А. Все члены класса Е наследуются с сохранением прав доступа. Наследованные классом А private-члены всех классов, как было показано выше, доступны только с помощью методов, унаследованных классом А от своих классов.
Поскольку множественное наследование является более сложной конструкцией по сравнению с единичным, то вполне очевидно, что при этом возникают и некоторые проблемы.
Множественное наследование позволяет строить сложные иерархические структуры, в которых может случиться так, что некоторый класс объявлен предком другого класса более чем один раз. Например:
class A {...};
class B : public A {...};
class С : public A {...};
class D : public A. public B, public С {...};
Здесь каждый объект класса D содержит наследованные члены класса А в трех экземплярах. Если это не нужно и мешает работе с объектом, то следует добавить описатель virtual при указании родительского класса:
class A {...};
class В : public virtual A {...};
class С : public virtual A {...};
class D : public A, public B, public C {...};
Теперь члены виртуального класса будут унаследованы классом D только в одном экземпляре.
В случае множественного наследования вполне вероятно, что один и тот же класс может унаследовать несколько объектов с одним и тем же именем из разных классов:
class Basel
{
int nObj; // Остальные компоненты первого базового класса
};
class Base2
{
int nObj;
// Остальные компоненты второго базового класса };
class Derived: public Basel, public Base2
{
// Компоненты производного класса
} clDer;
В данном случае имеется 2 объекта clDer.nObj с одним и тем же именем, а значит обращаться к ним просто по имени нельзя. Выход – в использовании оператора разрешения контекста и полных имен объектов:
clDer.BaseClass1::nObj++;
clDer.BaseClass2::nObj++;
Такое же решение проблемы необходимо использовать в случае наследования через промежуточный класс:
class BaseClass
{
int nBaseObj;
};
class FirstBaseClass: public BaseClass
{
int nFirstObj;
};
class SecondBaseClass: public BaseClass
{
int nSecondObj;
};
class DerivedClass: public FirstBaseClass, public SecondBaseClass
{
long nDerivedObj;
// Остальные компоненты производного класса
} clDer;
…
clDer.FirstBaseClass::nObj++;
clDer.SecondBaseClass::nObj ++;
При использовании наследования через промежуточный класс возникает другая проблема — при приведении типа указателя одного класса к другому может возникнуть неоднозначность:
DerivedClass clDerived;
DerivedClass *ptrDer = SclDerived;
BaseClass *ptrBase;
ptrBase = (Base *)ptrDer; // Ошибка — неоднозначность приведения типа
Проблема решается выполнением явного приведения типа к классу, для которого неоднозначностей не возникает
ptrBase = (Base *)(FirstBase *)ptrDer; // Теперь правильно
Тема 4.9. Полиморфизм в Visual C++.