
- •4.1 Введение 109
- •6.1 Введение и краткий обзор 170
- •7.1 Введение 206
- •8.1 Введение 232
- •9.1 Обработка ошибок 264
- •10.1 Введение 293
- •11.1 Введение 323
- •13.1 Введение 382
- •Предварительные замечания
- •Структура книги
- •Глава 6 посвящена понятию производных классов, которое позволяет строить
- •Раздел 3.4 главы 2. Для обозначения справочного руководства применяется
- •1.1 Введение
- •1.2 Парадигмы программирования
- •1.2.1 Процедурное программирование
- •1.2.2 Модульное программирование
- •1.2.3 Абстракция данных
- •1.2.4 Пределы абстракции данных
- •1.2.5 Объектно-ориентированное программирование
- •1.3 "Улучшенный с"
- •1.3.1 Программа и стандартный вывод
- •1.3.2 Переменные и арифметические операции
- •1.3.3 Указатели и массивы
- •1.3.4 Условные операторы и циклы
- •1.3.5 Функции
- •1.3.6 Модули
- •1.4 Поддержка абстракции данных
- •1.4.1 Инициализация и удаление
- •1.4.2 Присваивание и инициализация
- •1.4.3 Шаблоны типа
- •1.4.4 Обработка особых ситуаций
- •1.4.5 Преобразования типов
- •1.4.6 Множественные реализации
- •1.5 Поддержка объектно-ориентированного программирования
- •1.5.1 Механизм вызова
- •1.5.2 Проверка типа
- •1.5.3 Множественное наследование
- •1.5.4 Инкапсуляция
- •1.6 Пределы совершенства
- •* Глава 2. Описания и константы
- •2.1 Описания
- •2.1.1 Область видимости
- •2.1.2 Объекты и адреса
- •2.1.3 Время жизни объектов
- •2.2 Имена
- •2.3 Типы
- •2.3.1 Основные типы
- •2.3.2 Неявное преобразование типа
- •2.3.3 Производные типы
- •2.3.5 Указатели
- •2.3.6 Массивы
- •2.3.7 Указатели и массивы
- •2.3.8 Структуры
- •2.3.9 Эквивалентность типов
- •2.3.10 Ссылки
- •2.4 Литералы
- •2.4.1 Целые константы
- •2.4.2 Константы с плавающей точкой
- •2.4.3 Символьные константы
- •2.4.4 Строки
- •2.4.5 Нуль
- •2.5 Поименованные константы
- •2.5.1. Перечисления
- •2.6. Экономия памяти
- •2.6.1 Поля
- •2.6.2. Объединения
- •2.7 Упражнения
- •* Глава 3. Выражения и операторы
- •3.1 Калькулятор
- •3.1.1 Анализатор
- •3.1.2 Функция ввода
- •3.1.3 Таблица имен
- •3.1.4 Обработка ошибок
- •3.1.5 Драйвер
- •3.1.6 Параметры командной строки
- •3.2 Сводка операций
- •3.2.1 Скобки
- •3.2.2 Порядок вычислений
- •3.2.3 Инкремент и декремент
- •3.2.4 Поразрядные логические операции
- •3.2.5 Преобразование типа
- •3.2.6 Свободная память
- •3.3 Сводка операторов
- •3.3.1 Выбирающие операторы
- •3.3.2 Оператор goto
- •3.4 Комментарии и расположение текста
- •3.5 Упражнения
- •* Глава 4
- •4.1 Введение
- •4.2 Связывание
- •4.3 Заголовочные файлы
- •4.3.1 Единственный заголовочный файл
- •4.3.2 Множественные заголовочные файлы
- •4.4 Связывание с программами на других языках
- •4.5 Как создать библиотеку
- •4.6 Функции
- •4.6.1 Описания функций
- •4.6.2 Определения функций
- •4.6.3 Передача параметров
- •4.6.4 Возвращаемое значение
- •4.6.5 Параметр-массив
- •4.6.6 Перегрузка имени функции
- •4.6.7 Стандартные значения параметров
- •4.6.8 Неопределенное число параметров
- •4.6.9 Указатель на функцию
- •4.7 Макросредства
- •4.8 Упражнения
- •* Глава 5. Классы
- •5.1 Введение и краткий обзор
- •5.2 Классы и члены
- •5.2.1 Функции-члены
- •5.2.2 Классы
- •5.2.3 Ссылка на себя
- •5.2.4 Инициализация
- •5.2.5 Удаление
- •5.2.6 Подстановка
- •5.3 Интерфейсы и реализации
- •5.3.1 Альтернативные реализации
- •5.3.2 Законченный пример класса
- •5.4 Еще о классах
- •5.4.1 Друзья
- •5.4.2 Уточнение имени члена
- •5.4.3 Вложенные классы
- •5.4.4 Статические члены
- •5.4.5 Указатели на члены
- •5.4.6 Структуры и объединения
- •5.5 Конструкторы и деструкторы
- •5.5.1 Локальные переменные
- •5.5.2 Статическая память
- •5.5.3 Свободная память
- •5.5.4 Объекты класса как члены
- •5.5.5 Массивы объектов класса
- •5.5.6 Небольшие объекты
- •5.6 Упражнения
- •* Глава 6
- •6.1 Введение и краткий обзор
- •6.2 Производные классы
- •6.2.1 Функции-члены
- •6.2.2 Конструкторы и деструкторы
- •6.2.3 Иерархия классов
- •6.2.4 Поля типа
- •6.2.5 Виртуальные функции
- •6.3 Абстрактные классы
- •6.4 Пример законченной программы
- •6.4.1 Монитор экрана
- •6.4.2 Библиотека фигур
- •6.4.3 Прикладная программа
- •6.5 Множественное наследование
- •6.5.1 Множественное вхождение базового класса
- •6.5.2 Разрешение неоднозначности
- •6.5.3 Виртуальные базовые классы
- •6.6 Контроль доступа
- •6.6.1 Защищенные члены
- •6.6.2 Доступ к базовым классам
- •6.7 Свободная память
- •6.7.1 Виртуальные конструкторы
- •6.7.2 Указание размещения
- •6.8 Упражнения
- •* Глава 7
- •7.1 Введение
- •7.2 Операторные функции
- •7.2.1 Бинарные и унарные операции
- •7.2.2 Предопределенные свойства операций
- •7.2.3 Операторные функции и пользовательские типы
- •7.3 Пользовательские операции преобразования типа
- •7.3.1 Конструкторы
- •7.3.2 Операции преобразования
- •7.3.3 Неоднозначности
- •7.4 Литералы
- •7.5 Большие объекты
- •7.6 Присваивание и инициализация
- •7.7 Индексация
- •7.8 Вызов функции
- •7.9 Косвенное обращение
- •7.10 Инкремент и декремент
- •7.11 Строковый класс
- •7.12 Друзья и члены
- •7.13 Предостережения
- •7.14 Упражнения
- •* Глава 8. Шаблоны типа
- •8.1 Введение
- •8.2 Простой шаблон типа
- •8.3 Шаблоны типа для списка
- •8.3.1 Список с принудительной связью
- •8.3.2 Список без принудительной связи
- •8.3.3 Реализация списка
- •8.3.4 Итерация
- •8.4 Шаблоны типа для функций
- •8.4.1 Простой шаблон типа для глобальной функции
- •8.4.2 Производные классы позволяют ввести новые операции
- •8.4.3 Передача операций как параметров функций
- •8.4.4 Неявная передача операций
- •8.4.5 Введение операций с помощью параметров шаблонного класса
- •8.5 Разрешение перегрузки для шаблонной функции
- •8.6 Параметры шаблона типа
- •8.7 Шаблоны типа и производные классы
- •8.7.1 Задание реализации с помощью параметров шаблона
- •8.8 Ассоциативный массив
- •8.9 Упражнения
- •* Глава 9
- •9.1 Обработка ошибок
- •9.1.1 Особые ситуации и традиционная обработка ошибок
- •9.1.2 Другие точки зрения на особые ситуации
- •9.2 Различение особых ситуаций
- •9.3 Имена особых ситуаций
- •9.3.1 Группирование особых ситуаций
- •9.3.2 Производные особые ситуации
- •9.4 Запросы ресурсов
- •9.4.1 Конструкторы и деструкторы
- •9.4.2 Предостережения
- •9.4.3 Исчерпание ресурса
- •9.4.4 Особые ситуации и конструкторы
- •9.5 Особые ситуации могут не быть ошибками
- •9.6 Задание интерфейса
- •9.6.1 Неожиданные особые ситуации
- •9.7 Неперехваченные особые ситуации
- •9.8 Другие способы обработки ошибок
- •9.9 Упражнения
- •* Глава 10. Потоки
- •10.1 Введение
- •10.2 Вывод
- •10.2.1 Вывод встроенных типов
- •10.2.2 Вывод пользовательских типов
- •10.3 Ввод
- •10.3.1 Ввод встроенных типов
- •10.3.2 Состояния потока
- •10.3.3 Ввод пользовательских типов
- •10.4 Форматирование
- •10.4.1 Класс ios
- •10.4.1.1 Связывание потоков
- •10.4.1.2 Поля вывода
- •10.4.1.3 Состояние формата
- •10.4.1.4 Вывод целых
- •10.4.1.5 Выравнивание полей
- •10.4.1.6 Вывод плавающих чисел.
- •10.4.2 Манипуляторы
- •10.4.2.1 Стандартные манипуляторы ввода-вывода
- •10.4.3 Члены ostream
- •10.4.4 Члены istream
- •10.5 Файлы и потоки
- •10.5.1 Закрытие потоков
- •10.5.2 Строковые потоки
- •10.5.3 Буферизация
- •10.6 Ввод-вывод в с
- •10.7 Упражнения
- •* Проектирование и развитие
- •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 Зависимости в рамках иерархии классов.
- •12.2.4 Отношения принадлежности
- •12.2.5 Принадлежность и наследование
- •12.2.6 Отношения использования
- •12.2.7 Отношения внутри класса
- •12.2.7.1 Инварианты
- •12.2.7.2 Инкапсуляция
- •12.2.8 Программируемые отношения
- •12.3 Компоненты
- •12.4 Интерфейсы и реализации
- •12.5 Свод правил
- •* Проектирование библиотек
- •13.1 Введение
- •13.2 Конкретные типы
- •13.3 Абстрактные типы
- •13.4 Узловые классы
- •13.5 Динамическая информация о типе
- •13.5.1 Информация о типе
- •13.5.2 Класс Type_info
- •13.5.3 Как создать систему динамических запросов о типе
- •13.5.4 Расширенная динамическая информация о типе
- •13.5.5 Правильное и неправильное использование динамической
- •13.6 Обширный интерфейс
- •13.7 Каркас области приложения
- •13.8 Интерфейсные классы
- •13.9 Управляющие классы
- •13.10 Управление памятью
- •13.10.1 Сборщик мусора
- •13.10.2 Контейнеры и удаление
- •13.10.3 Функции размещения и освобождения
- •13.11 Упражнения
12.1.1 Игнорирование классов
Рассмотрим первый из указанных моментов - игнорирование классов.
В таком случае получившаяся программа на С++ будет приблизительно
эквивалентна С-программе, разработанной по тому же проекту, и,
можно сказать, что они будут приблизительно эквивалентны программам
на Аде или Коболе, разработанным по нему же.
По сути проект составлен как независящий от языка реализации, что
принуждает программиста ограничиваться общим подмножеством языков
С, Ада или Кобол. Здесь есть свои преимущества. Например, получившееся
в результате строгое разделение данных и программного кода позволяет
легко использовать традиционные базы данных, которые разработаны
для таких программ. Поскольку используется ограниченный язык
программирования, от программистов требуется меньше опытности
(или, по крайней мере другой ее уровень). Для многих приложений,
например, для традиционных баз данных, работающих с
файлом последовательно, такой подход вполне разумен, а традиционные
приемы, отработанные за десятилетия, вполне адекватны задаче.
Однако там, где область приложения существенно отличается от
традиционной последовательной обработки записей (или символов),
или сложность задачи выше, как, например, в диалоговой системе
CASE, недостаток языковой поддержки абстрактных данных
из-за отказа от классов (если их не учитывать) повредит
проекту. Сложность задачи не уменьшится, но, поскольку система
реализована на обедненном языке, структура программы плохо будет
отвечать проекту. У нее слишком большой объем, не хватает проверки типов,
и, вообще, она плохо приспособлена для использования различных
вспомогательных средств. Это путь, приводящий к кошмарам при ее
сопровождении.
Обычно для преодоления указанных трудностей создают специальные
средства, поддерживающие понятия, используемые в проекте. Благодаря
им создаются конструкции более высокого
уровня и организуются проверки с целью компенсировать дефекты
(или сознательное обеднение) языка реализации. Так метод
проектирования становится самоцелью, и для него создается специальный
язык программирования. Такие языки программирования в большинстве
случаев являются плохой заменой широко распространенных языков
программирования общего назначения, которые сопровождаются
подходящими средствами проектирования. Использовать С++ с таким
ограничением, которое должно компенсироваться при проектировании
специальными средствами, бессмысленно. Хотя несоответствие между
языком программирования и средствами проектирования может быть просто
стадией процесса перехода, а значит временным явлением.
Самой типичной причиной игнорирования классов при проектировании
является простая инерция. Традиционные языки программирования не
предоставляют понятия класса, и в традиционных методах проектирования
отражаются этот недостаток. Обычно в процессе проектирования
наибольшее внимание уделяется разбиению задачи на процедуры,
производящие требуемые действия. В главе 1 это понятие называлось
процедурным программированием, а в области проектирования оно
именуется как функциональная декомпозиция. Возникает типичный
вопрос "Можно ли использовать С++ совместно с методом проектирования,
базирующимся на функциональной декомпозиции?" Да, можно, но,
вероятнее всего, в результате вы придете к использованию С++ как
просто улучшенного С со всеми указанными выше проблемами. Это
может быть приемлемо на период перехода на новый язык, или для
уже завершенного проектирования, или для подзадач, в которых
использование классов не дает существенных выгод (если учитывать
опыт программирования на С++ к данному моменту), но в общем
случае на большом отрезке времени отказ от свободного
использования классов, связанный с методом функциональной
декомпозиции, никак не совместим с эффективным использованием С++.
Процедурно-ориентированный и объектно-ориентированный
подходы к программированию различаются по своей сути и обычно
ведут к совершенно разным решениям одной задачи. Этот вывод
верен как для стадии реализации, так и для стадии проектирования:
вы концентрируете внимание или на предпринимаемых действиях, или на
представляемых сущностях, но не на том и другом одновременно.
Тогда почему метод объектно-ориентированного проектирования
предпочтительнее метода функциональной декомпозиции?
Главная причина в том, что функциональная декомпозиция не дает
достаточной абстракции данных. А отсюда уже следует, что проект
будет
- менее податливым к изменениям,
- менее приспособленным для использования различных вспомогательных
средств,
- менее пригодным для параллельного развития и
- менее пригодным для параллельного выполнения.
Дело в том, что функциональная декомпозиция вынуждает объявлять
"важные" данные глобальными, поскольку, если система структурирована
как дерево функций, всякое данное, доступное двум функциям, должно
быть глобальным по отношению к ним. Это приводит к тому, что
"важные" данные "всплывают" к вершине дерева, по
мере того как все большее число функций требует доступа к нимЬ.
Ь В точности так же происходит в случае иерархии классов с одним
корнем, когда "важные" данные всплывают по направлению к базовому
классу.
Когда мы концентрируем внимание на описаниях классов, заключающих
определенные данные в оболочку, то зависимости между различными
частями программы выражены явно и можно их проследить. Еще более
важно то, что при таком подходе уменьшается число зависимостей
в системе за счет лучшей расстановки ссылок на данные.
Однако, некоторые задачи лучше решаются с помощью набора
процедур. Смысл "объектно-ориентированного" проектирования не в
том, чтобы удалить все глобальные процедуры из программы или
не иметь в системе процедурно-ориентированных частей. Основная
идея скорее в том, что классы, а не глобальные процедуры становятся
главным объектом внимания на стадии проектирования. Использование
процедурного стиля должно быть осознанным решением, а не решением,
принимаемым по умолчанию. Как классы, так и процедуры следует
применять сообразно области приложения, а не просто как
неизменные методы проектирования.