
- •Глава 6 посвящена понятию производных классов, которое позволяет строить
- •Раздел 3.4 главы 2. Для обозначения справочного руководства применяется
- •1991 Г.Г. (такие как множественное наследование, статические функции-члены
- •1.1 Введение
- •1.2 Парадигмы программирования
- •1.2.1 Процедурное программирование
- •1.2.5 Объектно-ориентированное программирование
- •1.5 Поддержка объектно-ориентированного программирования
- •1.5.1 Механизм вызова
- •1.5.2 Проверка типа
- •1.5.3 Множественное наследование
- •1.6 Пределы совершенства
- •2.2 Имена
- •2.3.2 Неявное преобразование типа
- •2.4 Литералы
- •2.4.4 Строки
- •2.6. Экономия памяти
- •2.6.1 Поля
- •3.1.1 Анализатор
- •3.1.2 Функция ввода
- •3.2 Сводка операций
- •3.2.3 Инкремент и декремент
- •3.2.5 Преобразование типа
- •3.2.6 Свободная память
- •3.3.2 Оператор goto
- •4.1 Введение
- •4.3.1 Единственный заголовочный файл
- •4.3.2 Множественные заголовочные файлы
- •4.4 Связывание с программами на других языках
- •4.6.3 Передача параметров
- •5.1 Введение и краткий обзор
- •5.3.1 Альтернативные реализации
- •5.3.2 Законченный пример класса
- •Vector и matrix, мы могли бы обойтись без контроля индекса при
- •5.4.5 Указатели на члены
- •5.4.6 Структуры и объединения
- •5.5.3 Свободная память
- •5.5.5 Массивы объектов класса
- •6.1 Введение и краткий обзор
- •6.2.3 Иерархия классов
- •6.2.4 Поля типа
- •6.2.5 Виртуальные функции
- •6.4.1 Монитор экрана
- •6.5 Множественное наследование
- •7.1 Введение
- •7.3 Пользовательские операции преобразования типа
- •7.3.2 Операции преобразования
- •7.3.3 Неоднозначности
- •7.5 Большие объекты
- •Void f2(t a) // вариант с контролем
- •Void f3(t a) // вариант с контролем
- •Inv() обращает саму матрицу m, а не возвращает новую, обратную m,
- •7.13 Предостережения
- •8.1 Введение
- •8.4.4 Неявная передача операций
- •8.4.5 Введение операций с помощью параметров шаблонного класса
- •8.7.1 Задание реализации с помощью параметров шаблона
- •9.1 Обработка ошибок
- •9.1.2 Другие точки зрения на особые ситуации
- •9.3.2 Производные особые ситуации
- •9.4.2 Предостережения
- •9.4.3 Исчерпание ресурса
- •9.4.4 Особые ситуации и конструкторы
- •9.5 Особые ситуации могут не быть ошибками
- •10.1 Введение
- •10.2 Вывод
- •10.2.1 Вывод встроенных типов
- •10.4.1.2 Поля вывода
- •10.4.1.4 Вывод целых
- •Istream - шаблон типа smanip, а smanip - двойник для ioss.
- •10.5.1 Закрытие потоков
- •10.5.2 Строковые потоки
- •X Целый параметр выдается в шестнадцатеричной записи;
- •11.1 Введение
- •11.2 Цели и средства
- •11.3 Процесс развития
- •11.3.1 Цикл развития
- •11.3.2 Цели проектирования
- •11.3.3 Шаги проектирования
- •11.3.3.1 Шаг 1: определение классов
- •11.3.3.2 Шаг 2: определение набора операций
- •11.3.3.3 Шаг 3: указание зависимостей
- •11.3.3.4 Шаг 4: определение интерфейсов
- •11.3.3.5 Перестройка иерархии классов
- •11.3.3.6 Использование моделей
- •11.3.4 Эксперимент и анализ
- •11.3.5 Тестирование
- •11.3.6 Сопровождение
- •11.3.7 Эффективность
- •11.4 Управление проектом
- •11.4.1 Повторное использование
- •11.4.2 Размер
- •11.4.3 Человеческий фактор
- •11.5 Свод правил
- •11.6 Список литературы с комментариями
- •12.1 Проектирование и язык программирования.
- •12.1.1 Игнорирование классов
- •12.1.2 Игнорирование наследования
- •12.1.3 Игнорирование статического контроля типов
- •12.1.4 Гибридный проект
- •12.2 Классы
- •12.2.1 Что представляют классы?
- •12.2.2 Иерархии классов
- •12.2.3 Зависимости в рамках иерархии классов.
- •Vertical_scrollbar или с помощью одного типа scrollbar, который
- •12.2.6 Отношения использования
- •12.2.7 Отношения внутри класса
- •12.3 Компоненты
- •12.4 Интерфейсы и реализации
- •12.5 Свод правил
- •13.1 Введение
- •13.2 Конкретные типы
- •13.4 Узловые классы
- •1, 2, 6 И 7. Класс, который не удовлетворяет условию 6, походит
- •13.5.1 Информация о типе
- •13.6 Обширный интерфейс
- •13.7 Каркас области приложения
- •13.8 Интерфейсные классы
- •13.10 Управление памятью
1, 2, 6 И 7. Класс, который не удовлетворяет условию 6, походит
на конкретный тип и может быть назван конкретным узловым классом.
Класс, который не удовлетворяет условию 7, походит на абстрактный
тип и может быть назван абстрактным узловым классом. У многих
узловых классов есть защищенные члены, чтобы предоставить для
производных классов менее ограниченный интерфейс.
Укажем на следствие условия 4: для трансляции своей программы
пользователь узлового класса должен включить описания всех его
прямых и косвенных базовых классов, а также описания
всех тех классов, от которых, в свою очередь, зависят базовые классы.
В этом узловой класс опять представляет контраст с абстрактным типом.
Пользователь абстрактного типа не зависит от всех классов,
использующихся для реализации типа и для трансляции своей программы
не должен включать их описания.
13.5 Динамическая информация о типе
Иногда бывает полезно знать истинный тип объекта до его использования
в каких-либо операциях. Рассмотрим функцию my(set&) из $$13.3.
void my_set(set& s)
{
for ( T* p = s.first(); p; p = s.next()) {
// мой код
}
// ...
}
Она хороша в общем случае, но представим,- стало известно,
что многие параметры множества представляют собой объекты типа
slist. Возможно также стал известен алгоритм перебора элементов, который
значительно эффективнее для списков, чем для произвольных
множеств. В результате эксперимента удалось выяснить, что именно
этот перебор является узким местом в системе. Тогда, конечно, имеет
смысл учесть в программе отдельно вариант с slist. Допустив возможность
определения истинного типа параметра, задающего множество, функцию
my(set&) можно записать так:
void my(set& s)
{
if (ref_type_info(s) == static_type_info(slist_set)) {
// сравнение двух представлений типа
// s типа slist
slist& sl = (slist&)s;
for (T* p = sl.first(); p; p = sl.next()) {
// эффективный вариант в расчете на list
}
}
else {
for ( T* p = s.first(); p; p = s.next()) {
// обычный вариант для произвольного множества
}
}
// ...
}
Как только стал известен конкретный тип slist, стали
доступны определенные операции со списками, и даже стала возможна
реализация основных операций подстановкой.
Приведенный вариант функции действует отлично, поскольку
slist - это конкретный класс, и действительно имеет смысл отдельно
разбирать вариант, когда параметр является slist_set. Рассмотрим
теперь такую ситуацию, когда желательно отдельно разбирать вариант как
для класса, так и для всех его производных классов. Допустим, мы
имеем класс dialog_box из $$13.4 и хотим узнать, является ли он
классом dbox_w_str. Поскольку может существовать много производных
классов от dbox_w_str, простую проверку на совпадение с ним
нельзя считать хорошим решением. Действительно, производные классы
могут представлять самые разные варианты запроса строки. Например,
один производный от dbox_w_str класс может предлагать пользователю
варианты строк на выбор, другой может обеспечить поиск в каталоге
и т.д. Значит, нужно проверять и на совпадение со всеми производными
от dbox_w_str классами. Это так же типично для узловых классов, как
проверка на вполне определенный тип типична для абстрактных классов,
реализуемых конкретными типами.
void f(dialog_box& db)
{
dbox_w_str* dbws = ptr_cast(dbox_w_str, &db);
if (dbws) { // dbox_w_str
// здесь можно использовать dbox_w_str::get_string()
}
else {
// ``обычный'' dialog_box
}
// ...
}
Здесь "операция" приведения ptr_cast() свой второй параметр
(указатель) приводит к своему первому параметру (типу) при условии, что
указатель настроен на объект тип, которого совпадает с заданным
(или является производным классом от заданного типа). Для проверки
типа dialog_box используется указатель, чтобы после приведения его
можно было сравнить с нулем.
Возможно альтернативное решение с помощью ссылки на dialog_box:
void g(dialog_box& db)
{
try {
dbox_w_str& dbws = ref_cast(dialog_box,db);
// здесь можно использовать dbox_w_str::get_string()
}
catch (Bad_cast) {
// ``обычный'' dialog_box
}
// ...
}
Поскольку нет приемлемого представления нулевой ссылки, с которой
можно сравнивать, используется особая ситуация, обозначающая ошибку
приведения (т.е. случай, когда тип не есть dbox_w_str). Иногда
лучше избегать сравнения с результатом приведения.
Различие функций ref_cast() и ptr_cast() служит хорошей
иллюстрацией различий между ссылками и указателями: ссылка обязательно
ссылается на объект, тогда как указатель может и не ссылаться,
поэтому для указателя часто нужна проверка.