
- •Глава 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.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 Управление памятью
12.2.2 Иерархии классов
Рассмотрим моделирование транспортного потока в городе, цель которого
достаточно точно определить время, требующееся, чтобы аварийные движущиеся
средства достигли пункта назначения. Очевидно, нам надо иметь
представления легковых и грузовых машин, машин скорой помощи,
всевозможных пожарных и полицейских машин, автобусов и т.п.
Поскольку всякое понятие реального мира не существует изолированно,
а соединено многочисленными связями с другими понятиями,
возникает такое отношение как наследование. Не разобравшись в понятиях
и их взаимных связях, мы не в состоянии постичь никакое отдельное
понятие. Также и модель, если не отражает отношения между
понятиями, не может адекватно представлять сами понятия. Итак, в
нашей программе нужны классы для представления понятий, но этого
недостаточно. Нам нужны способы представления отношений между классами.
Наследование является мощным способом прямого представления
иерархических отношений. В нашем примере, мы, по всей видимости,
сочли бы аварийные средства специальными движущимися средствами
и, помимо этого, выделили бы средства, представленные легковыми и
грузовыми машинами. Тогда иерархия классов приобрела бы такой вид:
движущееся средство
легковая машина аварийное средство грузовая машина
полицейская машина машина скорой помощи пожарная машина
машина с выдвижной лестницей
Здесь класс Emergency представляет всю информацию, необходимую для
моделирования аварийных движущихся средств, например: аварийная
машина может нарушать некоторые правила движения, она имеет
приоритет на перекрестках, находится под контролем диспетчера
и т.д.
На С++ это можно задать так:
class Vehicle { /*...*/ };
class Emergency { /* */ };
class Car : public Vehicle { /*...*/ };
class Truck : public Vehicle { /*...*/ };
class Police_car : public Car , public Emergency {
//...
};
class Ambulance : public Car , public Emergency {
//...
};
class Fire_engine : public Truck , Emergency {
//...
};
class Hook_and_ladder : public Fire_engine {
//...
};
Наследование - это отношение самого высокого порядка, которое прямо
представляется в С++ и используется преимущественно на ранних
этапах проектирования. Часто возникает проблема выбора: использовать
наследование для представления отношения или предпочесть ему
принадлежность. Рассмотрим другое определение понятия аварийного
средства: движущееся средство считается аварийным, если оно
несет соответствующий световой сигнал. Это позволит упростить
иерархию классов, заменив класс Emergency на член класса
Vehicle:
движущееся средство (Vehicle {eptr})
легковая машина (Car) грузовая машина (Truck)
полицейская машина (Police_car) машина скорой помощи (Ambulance)
пожарная машина (Fire_engine)
машина с выдвижной лестницей (Hook_and_ladder)
Теперь класс Emergency используется просто как член в тех классах,
которые представляют аварийные движущиеся средства:
class Emergency { /*...*/ };
class Vehicle { public: Emergency* eptr; /*...*/ };
class Car : public Vehicle { /*...*/ };
class Truck : public Vehicle { /*...*/ };
class Police_car : public Car { /*...*/ };
class Ambulance : public Car { /*...*/ };
class Fire_engine : public Truck { /*...*/ };
class Hook_and_ladder : public Fire_engine { /*...*/ };
Здесь движущееся средство считается аварийным, если Vehicle::eptr
не равно нулю. "Простые" легковые и грузовые машины инициализируются
Vehicle::eptr равным нулю, а для других Vehicle::eptr должно быть
установлено в ненулевое значение, например:
Car::Car() // конструктор Car
{
eptr = 0;
}
Police_car::Police_car() // конструктор Police_car
{
eptr = new Emergency;
}
Такие определения упрощают преобразование аварийного средства в
обычное и наоборот:
void f(Vehicle* p)
{
delete p->eptr;
p->eptr = 0; // больше нет аварийного движущегося средства
//...
p->eptr = new Emergency; // оно появилось снова
}
Так какой же вариант иерархии классов лучше? В общем случае ответ такой:
"Лучшей является программа, которая наиболее непосредственно отражает
реальный мир". Иными словами, при выборе модели мы должны стремиться
к большей ее"реальности", но с учетом неизбежных ограничений,
накладываемых требованиями простоты и эффективности. Поэтому,
несмотря на простоту преобразования обычного движущегося средства в
аварийное, второе решение представляется непрактичным.
Пожарные машины и машины скорой помощи - это
движущиеся средства специального назначения со специально
подготовленным персоналом, они действуют под управлением команд
диспетчера, требующих специального оборудования для связи. Такое
положение означает, что принадлежность к аварийным движущимся средствам -
это базовое понятие, которое для улучшения контроля типов и
применения различных программных средств должно быть прямо
представлено в программе. Если бы мы моделировали ситуацию, в которой
назначение движущихся средств не столь определенно,
скажем, ситуацию, в которой частный транспорт периодически используется
для доставки специального персонала к месту происшествия, а связь
обеспечивается с помощью портативных приемников, тогда мог бы
оказаться подходящим и другой способ моделирования системы.
Для тех, кто считает пример моделирования движения транспорта
экзотичным, имеет смысл сказать, что в процессе проектирования
почти постоянно возникает подобный выбор между наследованием
и принадлежностью. Аналогичный пример есть в $$12.2.5, где
описывается свиток (scrollbar) - прокручивание информации в окне.