- •Глава 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.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.4 Тип void
- •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.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.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.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.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.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.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.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.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 Упражнения
11.2 Цели и средства
Цель программирования - создать продукт, удовлетворяющий пользователя.
Важнейшим средством для достижении этой цели является создание
программы с ясной внутренней структурой и воспитание коллектива
программистов и разработчиков, имеющих достаточный опыт и мотивацию,
чтобы быстро и эффективно реагировать на все изменения.
Почему это так? Ведь внутрення структура программы и процесс, с
помощью которого она получена, в идеале никак не касаются конечного
пользователя. Более того, если конечный пользователь почему-то
интересуется тем, как написана программа, то что-то с этой программой
не так. Почему, несмотря на это, так важны структура программы и люди,
ее создавшие? В конце концов конечный пользователь ничего об этом
не должен знать.
Ясная внутренняя структура программы облегчает:
- тестирование,
- переносимость,
- сопровождение,
- расширение,
- реорганизацию и
- понимание.
Главное здесь в том, что любая удачная большая программа имеет
долгую жизнь, в течение которой над ней работают
поколения программистов и разработчиков, она переносится на новую
машину, приспосабливается к непредусмотренным требованиям и несколько
раз перестраивается. Во все время жизни необходимо в приемлемое время и
с допустимым числом ошибок выдавать версии программы. Не планировать все
это - все равно, что запланировать неудачу.
Отметим, что, хотя в идеальном случае случае пользователи не
должны знать внутреннюю структуру системы, на практике они обычно
хотят ее знать. Например, пользователь может желать познакомиться в
деталях с разработкой системы с целью научиться контролировать
возможности и надежность системы на случай переделок и расширений.
Если рассматриваемый программный продукт есть не полная система, а набор
библиотек для получения программных систем, то пользователь захочет
узнать побольше "деталей", чтобы они служили источником идей и
помогали лучше использовать библиотеку.
Нужно уметь очень точно определить объем проектирования программы.
Недостаточный объем приводит к бесконечному срезанию острых углов
("побыстрее передадим систему, а ошибку устраним в следующей версии").
Избыточный объем приводит к усложненному описанию системы, в котором
существенное теряется в формальностях, в результате чего при
реорганизации программы получение работающей версии затягивается ("новая
структура намного лучше старой, пользователь согласен ждать ради нее").
К тому же возникают такие потребности в ресурсах, которые непозволительны
для большинства потенциальных пользователей. Выбор объема
проектирования - самый трудный момент в разработке, именно здесь
проявляется талант и опыт. Выбор трудно сделать и для одного программиста
или разработчика, но он еще труднее для больших задач, где занято
много людей разного уровня квалификации.
Организация должна создавать программный продукт и сопровождать
его, несмотря на изменения в штате, в направлении работы или в
управляющей структуре. Распространенный способ решения этих проблем
заключался в попытке сведения процесса создания системы к нескольким
относительно простым задачам, укладывающимся в жесткую структуру.
Например, создать группу легко обучаемых (дешевых) и взаимозаменяемых
программистов низкого уровня ("кодировщиков") и группу не таких
дешевых, но взаимозаменяемых (а значит также не уникальных)
разработчиков. Считается, что кодировщики не принимают решений по
проектированию, а разработчики не утруждают себя "грязными"
подробностями кодирования. Обычно такой подход приводит к неудаче, а
где он срабатывает, получается слишком громоздкая система с плохими
характеристиками.
Недостатки такого подхода состоят в следующем:
- слабое взаимодействие между программистами и разработчиками
приводит к неэффективности, промедлению, упущенным возможностям и
повторению ошибок из-за плохого учета и отсутствия обмена опытом;
- сужение области творчества разработчиков приводит
к слабому профессиональному росту, безынициативности, небрежности и
большой текучести кадров.
По сути, подобные системы - это бесполезная трата редких человеческих
талантов. Создание структуры, в рамках которой люди могут найти
применение разным талантам, овладеть новым родом деятельности и
участвовать в творческой работе - это не только благородное дело, но
и практичное, коммерчески выгодное предприятие.
С другой стороны, нельзя создать систему, представить документацию
по ней и бесконечно ее сопровождать без некоторой жесткой организационной
структуры. Для чисто новаторского проекта хорошо начать с того, что
просто найти лучших специалистов и позволить им решать задачу в
соответствии с их идеями. Но по мере развития проекта требуется все
больше планирования, специализации и строго определенного взаимодействия
между занятыми в нем людьми. Под строго определенным понимается не
математическая или автоматически верифицируемая запись (хотя это
безусловно хорошо там, где возможно и применимо), а скорее набор
указаний по записи, именованию, документации, тестированию и т.п.
Но и здесь необходимо чувство меры. Слишком жесткая структура может
мешать росту и затруднять совершенствование. Здесь подвергается
проверке талант и опыт менеджера. Для отдельного работника аналогичная
проблема сводится к определению, где нужно проявить смекалку, а где
действовать по рецептам.
Можно рекомендовать планировать не на период до выдачи следующей
версии системы, а на более долгий срок. Строить планы только до
выпуска очередной версии - значит планировать неудачу. Нужно иметь
организацию и стратегию развития программного обеспечения, которые
нацелены на создание и поддержание многих версий разных систем, т.е.
нужно многократное планирование успеха.
Цель проектирования в выработке ясной и относительно простой
внутренней структуры программы, называемой иногда архитектурой, иными
словами каркаса, в который укладываются отдельные программные фрагменты,
и который помогает написанию этих фрагментов.
Проект - конечный результат процесса проектирования (если только
бывает конечный продукт у итеративного процесса). Он является
средоточием взаимодействий между разработчиком и программистом и
между программистами. Здесь необходимо соблюсти чувство меры. Если я,
как отдельный программист, проектирую небольшую программу, которую
собираюсь написать завтра, то точность и полнота описания проекта
может свестись к нескольким каракулям на обратной стороне конверта.
На другом полюсе находится система, над которой работают сотни
программистов и разработчиков, и здесь могут потребоваться тома
тщательно составленных спецификаций проекта на формальном или
полуформальном языке. Определение нужной степени точности, детализации
и формальности проектирования является уже само по себе нетривиальной
технической и административной задачей.
Далее будет предполагаться, что проект системы записывается
как ряд определений классов (в которых частные описания опущены
как лишние детали) и взаимоотношений между ними. Это упрощение, т.к.
конкретный проект может учитывать: вопросы параллельности, использование
глобального пространства имен, использование глобальных функций и
данных, построение программы для минимизации перетрансляции,
устойчивость, многомашинный режим и т.п. Но при обсуждении на данном
уровне детализации без упрощения не обойтись, а классы в контексте С++
являются ключевым понятием проектирования. Некоторые из указанных
вопросов будут обсуждаться ниже, а те, которые прямо затрагивают
проектирование библиотек С++, будут рассмотрены в главе 13. Более
подробное обсуждение и примеры определенных методов объектно-
ориентированного проектирования содержатся в [2].
Мы сознательно не проводили четкого разделения анализа и
проектирования, поскольку обсуждение их различий выходит за рамки этой
книги, и оно зависит от применяемых методов проектирования. Главное в том,
чтобы выбрать метод анализа, подходящий для метода проектирования, и
выбрать метод проектирования, подходящий для стиля программирования
и используемого языка.