- •Джесс Либерти
- •Об авторе
- •Посвящение
- •Благодарности
- •Введение
- •Для кого эта книга
- •Неделя №1 Основные вопросы
- •Несколько слов для программистов на языке c
- •Что дальше
- •День 1-й. Первые шаги Введение
- •Программы
- •Решение многих проблем
- •Процедурное, структурированное и объектно-ориентированное программирование
- •Инкапсуляция
- •Наследование и многократное использование
- •Полиморфизм
- •Нужно ли сначала изучить язык c
- •Стандарт ansi
- •Подготовка к программированию
- •Среда разработки
- •Компиляция исходного кода программы
- •Создание исполняемого файла с помощью компоновщика
- •Цикл разработки
- •Построение проекта приветствия
- •Ошибки компиляции
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •Кратко об объекте cout
- •Комментарии
- •Виды комментариев
- •Использование комментариев
- •Напоследок предупреждение: осторожнее с комментариями!
- •Функции
- •Использование функций
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 3-й. Переменные и константы
- •Что такое переменная
- •Резервирование памяти
- •Размер целых
- •Знаковые и беззнаковые типы
- •Базовые типы переменных
- •Определение переменной
- •Чувствительность к регистру букв
- •Ключевые слова
- •Создание нескольких переменных одного типа
- •Присваивание значений переменным
- •Ключевое слово typedef
- •В каких случаях следует использовать типы short и long
- •Переполнение беззнаковых целых
- •Переполнение знаковых целочисленных значений
- •Символы
- •Символы и числа
- •Специальные символы
- •Константы
- •Литеральные константы
- •Символьные константы
- •Определение констант с помощью директивы #deiine
- •Определение констант с помощью ключевого слова const
- •Константы перечислений
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 4-й. Выражения и операторы
- •Выражения
- •Символы пробелов
- •Блоки и комплексные варажения
- •Операции
- •Операторы
- •Оператор присваивания
- •Математические операторы
- •Целочисленное деление и деление по модулю
- •Совместное использование математических операторов с операторами присваивания
- •Инкремент декремент
- •Префикс и постфикс
- •Приоритеты операторов
- •Вложение круглых скобок
- •Что такое истинно
- •Операторы отношений
- •Оператор if
- •Использование отступов в программных кодых
- •Ключевое слово else
- •Сложные конструкции с if
- •Использование фигурных скобок для вложенных операторов if
- •Логические операторы
- •Логическое и
- •Логическое или
- •Логическое нет
- •Вычисление по сокращенной схеме
- •Приоритеты операторов отношений
- •Подробнее об истине и лжи
- •Условный оператор
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 5-й. Функции
- •Что такое функция
- •Возвращаемые значения, параметры и аргументы
- •Объявление и определение функций
- •Объявление функции
- •Прототипы функций
- •Определение функции
- •Выполнение функций
- •Локальные переменные
- •Глобальные переменные
- •Глобальные переменные; будьте начеку
- •Подробнее о локальных переменных
- •Операторы, используемые в функциях
- •Подробнее об аргументах функций
- •Использование функций в качестве параметров функций
- •Параметры - это локальные переменные
- •Подробнее о возвращаемых значениях
- •Значения параметров, используемые по умолчанию
- •Перегрузка функций
- •Дополнительные сведения о функциях
- •Подставляемые inline-функции
- •Рекурсия
- •Работа функций - приподнимаем завесу тайны
- •Уровни абстракции
- •Разбиение памяти
- •Стек и функции
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 6-й. Базовые классы
- •Создание новых типов
- •Зачем создавать новый тип
- •Классы и члены классов
- •Объявление класса
- •Несколько слов об используемых именах
- •Определение объекта
- •Классы в сравнении с объектами
- •Получение доступа к членам класса
- •Значения присваиваются объектам, а не классам
- •Что объявишь, то и будешь иметь
- •Ограничение доступа к членам класса
- •Оставьте данные класса закрытыми
- •Ограничение доступа к данным - это не способ защиты данных, а лишь средство облегчения программирования
- •Определение методов класса
- •Конструкторы и деструкторы
- •Конструкторы и деструкторы, заданные по умолчанию
- •Использование конструктора, заданного по умолчанию
- •Объявление функций-членов со спецификатором const
- •Чем отличается интерфейс от выполнения класса
- •Где следует распологать в программе объявления классов и определения методов
- •Выполнение с подстановкой
- •Классы содержащие другие классы в качестве данных-членов
- •Структуры
- •Почему два ключевых слова несут одинаковую смысловую нагрузку
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 7-й. Циклы
- •Организация циклов
- •История оператора goto
- •Почему следует избегать оператора goto
- •Организация циклов с помощью оператора while
- •Сложные конструкции с оператором while
- •Операторы break и continue
- •Использование конструкции while(true)
- •Организация циклов с помощью конструкции do...While
- •Использование конструкции do...While
- •Оператор for
- •Сложные выражения с оператором for
- •Множественные инициализации и приращения счетчиков цикла
- •Нулевые параметры цикла for
- •Использование пустых циклов for
- •Вложенные циклы
- •Область видимости переменных-счетчиков циклов for
- •Обобщение сведений о циклах
- •Оператор switch
- •Обработка комманд менб с помощью оператора switch
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •Подведение итогов
- •Итоги первой недели
- •Неделя №2 Основные вопросы
- •Что дальше
- •День 8-й. Указатели
- •Что такое указатель
- •Использование указателя как средства хранения адреса
- •Имена указателей
- •Оператор разыменовывания
- •Указатели, адреса и переменные
- •Обращение к данным через указатели
- •Использование адреса, хранящегося в указателе
- •Для чего нужны указатели
- •Память стековая и динамически распределяемая
- •Оператор new
- •Оператор delete
- •Что такое утечка памяти
- •Размещение объектов в области динамической памяти
- •Удаление объектов
- •Доступ к членам класса
- •Динамическое размещение членов класса
- •Указатель this
- •Блуждающие, дикие или зависшие указатели
- •Использование ключевого слова const при объявлении указателей
- •Использование ключевого слова const при объявлении указателей и функций-членов
- •Указатель const this
- •Вычисления с указателями
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 9-й. Ссылки
- •Что такое ссылка
- •Использование оператора адреса (&) при работе со ссылками
- •Ссылки нельзя переназначать
- •На что можно ссылаться
- •Нулевые указатели и нулевые ссылки
- •Передача аргументов функций как ссылок
- •Передача указателей в функцию swap()
- •Передача ссылок в функцию swap()
- •Представления о заголовках функций и прототипах
- •Возвращение нескольких значений
- •Возвращение значений с помощью ссылок
- •Передача ссылок на переменные как средство повышения эффективности
- •Передача константного указателя
- •Ссылки в качестве альтернативы
- •Когда лучше использовать ссылки, а когда - указатели
- •Коктейль из ссылок и указателей
- •Не возвращайте ссылку на объект, который находиться вне области видимости!
- •Возвращение ссылки на в области динамического обмена
- •А где же уазатель?
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 10-й. Дополнительные возможности использования функции
- •Перегруженные функции-члены
- •Использование значений, заданных по умолчанию
- •Выбор между значениями по умолчанию и перегруженными функциями
- •Конструктор, принятый по умолчанию
- •Перегрузка конструкторов
- •Инициализация объектов
- •Конструктор-копировщик
- •Перегрузка операторов
- •Запись Функции инкремента
- •Перегрузка префиксных операторов
- •Типы возвратов перегруженных функций операторов
- •Возвращение безымянных временных объектов
- •Использование указателя this
- •Перегрузка постфиксных операторов
- •Различия между преинкрементном и постинкрементном
- •Оператор суммирования
- •Перегрузка оператора суммирования
- •Основные принципы перегрузки операторов
- •Ограничения перегрузки операторов
- •Что можно перегружать
- •Оператор присваивания
- •Операторы преобразований
- •Операторы преобразования типов
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 11-й. Наследование
- •Что такое наследование
- •Иерархия и наследование
- •Царство животных
- •Синтаксис наследования классов
- •Закрытый или защищенный
- •Конструкторы и деструкторы
- •Передача аргументов в базовые конструкторы
- •Замещение функций
- •Сокрытие метода базового класса
- •Вызов базового метода
- •Виртуальные методы
- •Как работают виртуальные методы
- •Нельзя брать там, находясь здесь
- •Дробление объекта
- •Виртуальные деструкторы
- •Виртуальный конструктор-копировщик
- •Цена виртуальности методов
- •Вопросы и ответы
- •Коллоквиум
- •Упражнения
- •День 12-й. Массивы и связанные листы
- •Что такое массивы
- •Элементы массива
- •Вывод данных за пределами массива
- •Ошибки подсчета столбцов для забора
- •Инициализация массива
- •Объявление массивов
- •Массивы объектов
- •Многомерные массивы
- •Инициализация многомерного массива
- •Несколько слов о памяти
- •Массивы указателей
- •Объявление массивов в области динамического обмена
- •Указатель на массив или массив указателей
- •Имена массивов и указателей
- •Удаление массива из области динамической памяти
- •Массивы символов
- •Функции strcpy() и strncpy()
- •Классы строк
- •Связанные списки и другие структуры
- •Общие представления о связанных списках
- •Делегирование ответственности
- •Компоненты связанных списков
- •Что мы узнали в этой главе
- •Классы массивов
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 13-й. Полиморфизм
- •Проблемы с одиночным наследованием
- •Перенос метода вверх по иерархии классов
- •Приведение указателя к типу производного класса
- •Добавление объекта в два списка
- •Множественное наследование
- •Из каких частей состоят объекты, полученные в результате множественного наследования
- •Конструкторы классов, полученных в результате множественного наследования
- •Двусмысленность ситуации
- •Наследование от общего базового класса
- •Виртуальное наследование
- •Проблемы с множественным наследованием
- •Классы-мандаты
- •Абстрактные типы данных
- •Чистые виртуальные функции
- •Выполнение чистых виртуальных функций
- •Сложная иерархия абстракций
- •Когда следует использовать абстрактные типы данных
- •Логика использования абстрактных классов
- •Пара слов о множественном наследовании, абстрактных типах данных и языке Java
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 14-й. Специальные классы и функции
- •Статические переменные-члены
- •Статические функции-члены
- •Указатели на функции
- •Зачем нужны указатели на функции
- •Массивы указателей на функции
- •Передача указателей на функции в другие функции
- •Использование typedef с указателями на функции
- •Указатели на функции члены
- •Массивы указателейна функции-члены
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •Подведение итогов
- •Неделя №3 Основные вопросы
- •Что дальше
- •День 15-й. Дополнительные возможности наследования
- •Вложение
- •Доступ к членам вложенного класса
- •Фильтрация доступа к вложенным классам
- •Цена вложений
- •Передача объекта как значения
- •Различные пути передачи функциональности классу
- •Делегирование
- •Закрытое наследование
- •Классы друзья
- •Функции друзья
- •Функции друзья и перегрузка оператора
- •Перегрузка оператора вывода
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 16-й. Потоки
- •Знакомство с потоками
- •Инкапсуляция
- •Буферизация
- •Потоки и буферы
- •Стандартные объекты ввода-вывода
- •Переадресация
- •Вывод данных с помощью cin
- •Проблемы, возникающие при вводе строк
- •Другие методы объекта cin
- •Ввод одного символа
- •Использование функции gef() без параметров
- •Использование функции get() с параметром
- •Ввод строк со стандартного устройства ввода
- •Использование функции cin.Ignore()
- •Функции-члены peek() и putback()
- •Ввод данных с помощью cout
- •Очистка буфера вывода
- •Функции-члены объекта cout
- •Манипуляторы, флаги и команды форматирования
- •Использование функции cout.Width()
- •Установка символов заполнения
- •Установка флагов
- •Сравнение потоков и функции printf()
- •Использование файлов для ввода и вывода данных
- •Объекты ofstream
- •Состояния условий
- •Открытие файлов для ввода-вывода
- •Настройка открытия файла объектом ofstream
- •Двоичные и тектовые файлы
- •Установка параметров ввода-вывода с помощью коммандной строки
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 17-й. Пространства имен
- •Введение
- •Вызов по имени функций и классов
- •Создание пространства имен
- •Объявление и определение типов
- •Объявление функций за пределами пространства имен
- •Добавление новых членов
- •Вложения пространства имен
- •Использование пространства имен
- •Ключевое слово using
- •Использование using как оператора
- •Использование using в объявлениях
- •Псевдонимы пространства имен
- •Неименованные пространства имен
- •Стандартное пространство имен std
- •Вопросы и ответы
- •Построение моделей
- •Проектирование программ: язык моделирования
- •Процесс проектирования программ
- •Анализ требований
- •Ситуация использования
- •Определение пользователей
- •Определение первой ситуации использования
- •Создание модели домена
- •Обобщение
- •Вложение
- •Ассоциация
- •Разработка сценариев
- •Разработка путеводителей
- •Диаграммы взаимодействий
- •Создание пакетов
- •Анализ совместимости приложения
- •Анализ существующих систем
- •Прочая документация
- •Визуализация
- •Артефакты
- •Проектирование
- •Что такое классы
- •Преобразования
- •Другие преобразования
- •Обработка данных
- •Устройства
- •Статическая модель
- •Карточки crc
- •Как проводить заседания с карточками
- •Сфокусируемся на распределении ответственности
- •Как сделать класс живым
- •Ограничения карточек crc
- •Создание модели uml no картонкам crc
- •Отношения между классами
- •Множественное наследование против вложения
- •Дискриминаторы и силовые классы
- •Динамическая модель
- •Диаграммы переходов состояний
- •Сверхсостояния
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 19-й. Шаблоны
- •Что такое шаблоны
- •Параметризованные типы
- •Создание экземпляра шаблона
- •Объявление шаблона
- •Использование имени шаблона
- •Выполнение шаблона
- •Функции шаблона
- •Шаблоны и друзья
- •Дружественные классы и функции, не являющиеся шаблонами
- •Дружественный класс или функция как общий шаблон
- •Использование экземпляров шаблона
- •Специализированные функции
- •Статические члены и шаблоны
- •Стандартная библиотека шаблонов
- •Контейнеры
- •Последовательные контейнеры
- •Контейнер двухсторонней очереди
- •Очередь
- •Ассоциативные контейнеры
- •Другие ассоциативные контейнеры
- •Классы алгоритмов
- •Операции, не изменяющие последовательность
- •Алгоритмы изменения последовательности
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 20-й. Отслеживание исключительных ситуаций и ошибок
- •Ошибки, погрешности, ляпусы и "гнилой" код
- •Исключительные ситуации
- •Несколько слов о "гнилом" коде
- •Исключения
- •Как используются исключения
- •Использование блоков try и catch
- •Перехват исключений
- •Использование нескольких операторов catch
- •Наследование исключений
- •Данные в классах исключений и присвоение имен объектам исключений
- •Исключения и шаблоны
- •Исключения без ошибок
- •Ошибки и отладка программы
- •Точка останова
- •Анализ значений переменных
- •Исследование памяти
- •Код ассемблера
- •Вопросы и ответы
- •Коллоквиум
- •Контрольные вопросы
- •Упражнения
- •День 21-й. Что дальше
- •Процессор и компилятор
- •Просмотр промежуточного файла
- •Использование директивы #define
- •Использование директивы #define для создания констант
- •Использование директивы #define для тестирования
- •Комманда препроцессора #else
- •Включение файлов и предупреждение ошибок включения
- •Макросы
- •Зачем нужны все эти круглые скобки
- •Макросы в сравнении с функциями шаблонов
- •Подставляемые функции
- •Операции со строками
- •Оператор взятия в кавычки
- •Конкатенация
- •Встроенные макросы
- •Макрос assert()
- •Оладка программы с помощью assert()
- •Макрос assert() вместо исключений
- •Побочные эффекты
- •Инварианты класса
- •Печать промежуточных значений
- •Уровни отладки
- •Операции с битами данных
- •Сброс битов
- •Инверсия битов
- •Битовые поля
- •Стиль программирования
- •Отступы
- •Фигурные скобки
- •Длинные строки
- •Конструкции с оператором switch
- •Текст программы
- •Имена идентификаторов
- •Правописание и использование прописных букв в именах
- •Комментарии
- •Организация доступа к данным и методам
- •Определения классов
- •Включение файлов
- •Макрос assert()
- •Ключевое слово const
- •Сделаем еще один шаг вперед
- •Где получить справочную информацию и советы
- •Журналы
- •Выскажите свое мнение о книге
- •Вопросы и ответы
- •Коллоквиум Контрольные вопросы
- •Упражнения
- •Подведение итогов
- •Приложение а Приоритеты операторов
- •Приложение б
- •Приложение в Двоичные о числа
- •Другие системы счисления
- •Еще об основаниях
- •Двоичная система счисления
- •Почему именно основание 2
- •Биты, байты и полубайты
- •Что такое килобайт
- •Двоичные числа
- •Шестнадцатерична система счисления
- •Приложение г - Ответы День 1 Контрольные вопросы
- •Упражнения
- •День 2 Контрольные вопросы
- •Упражнения
- •День 3 Контрольные вопросы
- •Упражнения
- •День 4 Контрольные вопросы
- •Упражнения
- •День 5 Контрольные вопросы
- •Упражнения
- •День 6 Контрольные вопросы
- •Упражнения
- •День 7 Контрольные вопросы
- •Упражнения
- •День 8 Контрольные вопросы
- •Упражнения
- •День 9 Контрольные вопросы
- •Упражнения
- •День 10 Контрольные вопросы
- •Упражнения
- •День 11 Контрольные вопросы
- •Упражнения
- •День 12 Контрольные вопросы
- •Упражнения
- •День 13 Контрольные вопросы
- •Упражнения
- •День 14 Контрольные вопросы
- •Упражнения
- •День 15 Контрольные вопросы
- •Упражнения
- •День 16 Контрольные вопросы
- •Упражнения
- •День 17 Контрольные вопросы
- •Упражнения
- •День 18 Контрольные вопросы
- •Упражнения
- •День 19 Контрольные вопросы
- •Упражнения
- •День 20 Контрольные вопросы
- •Упражнения
- •День 21 Контрольные вопросы
- •Упражнения
Подведение итогов
В этой главе вашему вниманию предлагается достаточно мощная программа, в которой используется большинство средств и подходов программирования, освоенных вами в течение двух недель.
В этой программе используются связанные списки, виртуальные функции, чистые виртуальные функции, замещения функций, полиморфизм, открытое наследование, перегрузка функций, вечные циклы, указатели, ссылки и многие другие знакомые вам средства. Обратите внимание, что представленный здесь связанный список отличается от рассмотренных ранее. Язык C++ предоставляет множество способов достижения одной и той же цели.
Цель данной программы состоит в создании функционального связанного списка. В узлах созданного списка можно хранить записи о деталях и агрегатах, что позволяет использовать его в реальных прикладных программах баз данных складов. Хотя здесь представлена не окончательная форма программы, она достаточно хорошо демонстрирует возможности создания совершенной структуры накопления и обработки данных. Листинг программы содержит 311 строк. Попробуйте самостоятельно проанализировать код, прежде чем прочтете анализ, приведенный после листинга.
Итоги второй недели
1: // **********************************
2: //
3: // Название: Подведение итогов
4: //
5: // Файл: Week2
6: //
7: // Описание: Демонстрация создания и использования связанного списка
8: //
9: // Классы: PART - содержит идентификационный
10: // номер детали и обеспечивает возможность
11: // добавлять другие данные
12: // PartNode - функционирует как узел в PartsList
13: //
14: // PartsList - реализует механизм связывания
15: // узлов в список
16: //
17: // **********************************
18:
19: #include <iostream.h>
20:
21:
22:
23: // **************** Part ************
24:
25: // Абстрактный базовый класс, общий для всех деталей
26: class Part
27: {
28: public:
29: Part():itsPartNumber(1) { }
30: Part(int PartNumber):itsPartNumber(PartNumber) { }
31: virtual ~Part() { };
32: int GetPartNumber() const { return itsPartNumber; }
33: virtual void Display() const =0; // должна быть замещена как private
34: private:
35: int itsPartNumber;
36: };
37:
38: // выполнение чистой виртуальной функции в
39: // стандартном виде для всех производных классов
40: void Part::Display() const
41: {
42: cout << "\nНомер детали: " << itsPartNumber << endl;
43: }
44:
45: // ************* Автомобильные детали **********
46:
47: class CarPart : public Part
48: {
49: public:
50: CarPart():itsModelYear(94){ }
51: CarPart(int year, int partNumber);
52: virtual void Display() const
53: {
54: Part::Display(); cout << "Год создания: ";
55: cout << itsModelYear << endl;
56: }
57: private:
58: int itsModelYear;
59: };
60:
61: CarPart::CarPart(int year, int partNumber):
62: itsModelYear(year),
63: Part(partNumber)
64: { }
65:
66:
67: // ************* Авиационные детали **********
68:
69: class AirPlanePart : public Part
70: {
71: public:
72: AirPlanePart():itsEngineNumber(1){ } ;
73: AirPlanePart(int EngineNumber, int PartNumber);
74: virtual void Display() const
75: {
76: Part::Display(); cout << "Номер двигателя: ";
77: cout << itsEngineNumber << endl;
78: }
79: private:
80: int itsEngineNumber;
81: };
82:
83: AirPlanePart::AirPlanePart(int EngineNumber, intPartNumber):
84: itsEngineNumber(EngineNumber),
85: Part(PartNumber)
86: { }
87:
88: // ************** Узлы списка деталей **********
89: class PartNode
90: {
91: public:
92: PartNode (Part*);
93: ~PartNode();
94: void SetNext(PartNode * node) { itsNext = node; }
95: PartNode * GetNext() const;
96: Part * GetPart() const;
97: private:
98: Part *itsPart;
99: PartNode * itsNext;
100: };
101:
102: // Выполнение PartNode...
103:
104: PartNode::PartNode(Part* pPart):
105: itsPart(pPart),
106: itsNext(0)
107: { }
108:
109: PartNode::~PartNode()
110: {
111: delete itsPart;
112: itsPart = 0;
113: delete itsNext;
114: itsNext = 0;
115: }
116:
117: //Возвращается NULL, если нет следующего узла PartNode
118: PartNode * PartNode::GetNext() const
119: {
120: return itsNaxt;
121: }
122:
123: Part * PartNode::GetPart() const
124: {
125: if (itsPart)
126: return itsPart;
127: else
128: return NULL; // ошибка
129: }
130:
131: // **************** Список деталей ************
132: class PartsList
133: {
134: public:
135: PartsList();
136: ~PartsList();
137: // Необходимо, чтобы конструктор-копировщик и оператор соответствовали друг другу!
138: Part* Find(int & position, int PartNumber) const;
139: int GetCount() const { return itsCount; }
140: Part* GetFirst() const;
141: static PartsList& GetGlobalPartsList()
142: {
143: return GlobalPartsList;
144: }
145: void Insert(Part *);
146: void Iterate(void (Part::*f)()const) const;
147: Part* operator[](int) const;
148: private:
149: PartNode * pHead;
150: int itsCount;
151: static PartsList GlobalPartsList;
152: };
153:
154: PartsList PartsList::GlobalPartsList;
155:
156: // Выполнение списка...
157:
158: PartsList::PartsList():
159: pHead(0),
160: itsCount(0)
161: { }
162:
163: PartsList::^PartsList()
164: {
165: delete pHead;
166: }
167:
168: Part* PartsList::GetFirst() const
169: {
170: if (pHead)
171: return pHead->GetPart();
172: else
173: return NULL; // ловушка ошибок
174: }
175:
176: Part * PartsList::operator[](int offSet) const
177: {
178: PartNode* pNode = pHead;
179:
180: if (!pHead)
181: return NULL; // ловушка ошибок
182:
183: if (offSet > itsCount)
184: return NULL; // ошибка
185:
186: for (int i=0;i<offSet; i++)
187: pNode = pNode->GetNext();
188:
189: return pNode->GetPart();
190: }
191:
192: Part* PartsList::Find(int & position, int PartNumber) const
193: {
194: PartNode * pNode = 0;
195: for (pNode = pHead, position = 0;
196: pNode!=NULL;
197: pNode = pNode->GetNext(), position++)
198: {
199: if (pNode->GetPart()->GetPartNumber() == PartNumber)
200: break;
201: }
202: if (pNode == NULL)
203: return NULL;
204: else
205: return pNode->GetPart();
206: }
207:
208: void PartsList::Iterate(void (Part::*func)()const) const
209: {
210: if (!pHead)
211: return;
212: PartNode* pNode = pHead;
213: do
214: (pNode->GetPart()->*func)();
215: while (pNode = pNode->GetNext());
216: }
217:
218: void PartsList::Insert(Part* pPart)
219: {
220: PartNode * pNode = new PartNode(pPart);
221: PartNode * pCurrent = pHead;
222: PartNode >> pNext = 0;
223:
224: int New = pPart->GetPartNumber();
225: int Next = 0;
226: itsCount++;
227:
228: if (!pHead)
229: {
230: pHead = pNode;
231: return;
232: }
233:
234: // Если это значение меньше головного узла,
235: // то текущий узел становится головным
236: if (pHead->GetPart()->GetPartNumber()->New)
237: {
238: pNode->SetNext(pHead);
239: pHead = pHode;
240: return;
241: }
242:
243: for (;;)
244: {
245: // Если нет следующего, вставляется текущий
246: if (!pCurrent->GetNext())
247: {
248: pCurrent->SetNext(pNode);
249: return;
250: }
251:
252: // Если текущий больше предыдущего, но меньше следующего, то вставляем
253: // здесь. Иначе присваиваем значение указателя Next
254: pNext = pCurrent->GetNext();
255: Next = pNext->GetPart()->GetPartNumber();
256: if (Next > New)
257: {
258: pCurrent->SetNext(pNode);
259: pNode->SetNext(pNext);
260: return;
261: }
262: pCurrent = pNext;
263: }
264: }
265:
266: int main()
267: {
268: PartsList&pl = PartsList::GetGlobalPartsList();
269: Part * pPart = 0;
270: int PartNumber;
271: int value;
272: int Choice;
273:
274: while (1)
275: {
276: cout << "(0)Quit (1)Car (2)Plane: ";
277: cin >> choice;
278:
279: if (!choice)
280: break;
281:
282: cout << "New PartNumber?: ";
283: cin >> PartNumber;
284:
285: if (choice == 1)
286: {
287: cout << "Model Year?: ";
288: cin >> value;
289: pPart = new CarPart(value,PartNumber);
290: }
291: else
292: {
293: cout << "Engine Number?: ";
294: cin >> value;
295: pPart = new AirPlanePart(value,PartNumber);
296: }
297:
298: pl.Insert(pPart);
299: }
300: void (Part::*pFunc)()const = Part::Display;
301: pl.Iterate(pFunc);
302: return 0;
303: }
Результат:
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 2837
Model Year? 90
(0)Quit (1)Car (2)Plane: 2
New PartNumber?: 378
Engine Number?: 4938
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 4499
Model Year?: 94
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 3000
Model Year? 93
(0)Quit (1)Car (2)Plane: 0
Part Number: 378
Engine No.: 4938
Part Number: 2837
Model Year: 90
Part Number: 3000
Model Year: 93
Part Number: 4499
Model Year: 94
Представленная программа создает связанный список объектов класса Part. Связанный список — это динамическая структура данных вроде массива, за исключением того, что в список можно добавлять произвольное число объектов указанного типа и удалять любой из введенных объектов.
Данный связанный список разработан для хранения объектов класса Part, где Part — абстрактный тип данных, служащий базовым классом для любого объекта с заданной переменной-членом itsPartNumber. В программе от класса Part производятся два подкласса - CarPartHAirPlanePart.
Класс Part описывается в строках 26—36, где задаются одна переменная-член и несколько методов доступа. Предполагается, что затем в объекты класса будет добавлена другая ценная информация и возможность контроля за числом созданных объектов в базе данных. Класс Part описывается как абстрактный тип данных, на что указывает чистая виртуальная функция Display().
Обратите внимание, что в строках 40-43 определяется выполнение чистой виртуальной функции Display(). Предполагается, что метод Display() будет замещаться в каждом производном классе, но в определении замещенного варианта допускается просто вызывать стандартный метод из базового класса.
Два простых производных класса, CarPart и AirPlanePart, описываются в строках 47—59 и 69—87 соответственно. В каждом из них замещается метод Display() простым обращением к методу Display() базового класса.
Класс PartNode выступает в качестве интерфейса между классами Part и PartList. Он содержит указатель на Part и указатель на следующий узел в списке. Методы этого класса только возвращают и устанавливают следующий узел в списке и возвращают соответствующий объект Part.
За "интеллектуальность" связанного списка полностью отвечает класс PartsList, описываемый в строках 132—152. Этот класс содержит указатель на головной узел списка (pHead) и, с его помощью продвигаясь по списку, получает доступ ко всем другим методам. Продвижение по списку означает запрашивание текущего узла об адресе следующего вплоть до обнаружения узла, указатель которого на следующий узел равен NULL.
Безусловно, в этом примере представлен упрощенный вид связанного списка. В реально используемой программе список должен обеспечивать еще больший доступ к первому и последнему узлам списка или создавать специальный объект итерации, с помощью которого клиенты смогут легко продвигаться по списку.
В то же время класс PartsList предлагает ряд интересных методов, упорядоченных по алфавиту. Зачастую такой подход весьма эффективен, поскольку упрощает поиск нужных функций.
Функция Find() принимает в качестве параметров PartNumber и значение int. Если найден раздел с указанным значением PartNumber, функция возвращает указатель на Part и порядковый номер этого раздела в списке. Если же раздел с номером PartNumber не обнаружен, функция возвращает значение NULL.
Функция GetCount() проходит по всем узлам списка и возвращает количество объектов в списке. В PartsList это значение записывается в переменную-член itsCount, хотя его можно легко вычислить, последовательно продвигаясь no списку.
Функция GetFirst() возвращает указатель на первый объект Part в списке или значение NULL, если список пустой.
Функция GetGlobalPartsList() возвращает ссылку на статическую переменную-член GiobalPartsList. Описание статической переменной GlobaiPartsList является типичным решением для классов типа PartsList, хотя, безусловно, могут использоваться и другие имена. В законченном виде реализация этой идеи состоит в автоматическом изменении конструктора класса Part таким образом, чтобы каждому новому объекту класса присваивался номер с учетом текущего значения статической переменной GiobalPartsList.
Функция Insert принимает значение указателя на объект Part, создает для него PartNode и добавляет объект Part в список в порядке возрастания номеров PartNumber.
Функция Iterate принимает указатель на константную функцию-член класса Part без параметров, которая возвращает void. Эта функция вызывается для каждого объекта Part в списке. В описании класса Part таким характеристикам соответствует единственная функция Display(), замещенная во всех производных классах. Таким образом, будет вызываться вариант метода Display(), соответствующий типу объекта Part.
Функция Operator[] позволяет получить прямой доступ к объекту Part по заданному смещению. Этот метод обеспечивает простейший способ определения границ списка: если список нулевой, или заданное смещение больше числа объектов в списке, возвращается значение NULL, сигнализирующее об ошибке.
В реальной программе имело бы смысл все эти комментарии с описанием назначений функций привести в описании класса.
Тело функции main() представлено в строках 266-303. В строке 268 описывается ссылка на PartsList и инициализируется значением GiobalPartsList. Обратите внимание, что GiobalPartsList инициализируется в строке 154. Эта строка необходима, поскольку описание статической переменной-члена не сопровождается ее автоматическим определением. Поэтому определение статической переменной-члена должно выполняться за пределами описания класса.
В строках 274—299 пользователю предлагается указать, вводится ли деталь для машины или для самолета. В зависимости от выбора, запрашиваются дополнительные сведения и создается новый объект, который добавляется в список в строке 298.
Выполнение метода Insert() класса PartList показано в строках 218—264. При вводе идентификационного номера первой детали — 2837 — создается объект CarPart, который передается в LinkedList::Insert()c введенными номером детали и годом создания 90.
В строке 220 создается новый объект PartNode, принимающий значения новой детали. Переменная New инициализируется номером детали. Переменная-член itsCount класса PartsList увеличивается на единицу в строке 226.
В строке 228 проверяется равенство указателя pHead значению NULL. В данном случае возвращается значение TRUE, поскольку это первый узел списка и указатель pHead в нем нулевой. В результате в строке 230 указателю pHead присваивается адрес нового узла и функция возвращается.
Пользователю предлагается ввести следующую деталь. В нашем примере вводится деталь от самолета с идентификационным номером 37 и номером двигателя 4938. Снова вызывается функция PartsList::Insert() и pNode инициализируется новым узлом. Статическая переменная-член itsCount становится равной 2 и вновь проверяется pHead. Поскольку теперь pHead не равен нулю, то значение указателя больше не изменяется.
В строке 236 номер детали, указанный в головном узле, на который ссылается pHead (в нашем случае это 2837), сравнивается с номером новой детали — 378. Поскольку последний номер меньше, условное выражение в строке 236 возвращает TRUE и головным узлом в списке становится новый объект.
Строкой 238 указателю pNode присваивается адрес того узла, на который ссылался указатель pHead. Обратите внимание, что в следующий узел списка передается не новый объект, а тот, который был введен ранее. В строке 239 указателю pHead присваивается адрес нового узла.
На третьем цикле пользователь вводит деталь для автомобиля под номером 4499 с годом выпуска 94. Происходит очередное приращение счетчика и сравнивается номер текущего объекта с объектом головного узла. В этот раз новый введенный идентификационный номер детали оказывается больше номера объекта, определяемого в pHead, поэтому запускается цикл for в строке 243.
Значение идентификационного номера головного узла равно 378. Второй узел содержит объект со значением 2837. Текущее значение — 4499. Исходно указатель pCurrent связывается с головным узлом. Поэтому при обращении к переменной next объекта, на который указывает pCurrent, возвращается адрес второго узла. Следовательно, условное выражение в строке 246 возвратит False.
Указатель pCurrent устанавливается на следующий узел, и цикл повторяется. Теперь проверка в строке 246 приводит к положительному результату. Если следующего элемента нет, то новый узел вставляется в конец списка.
На четвертом цикле вводится номер детали 3000. Дальнейшее выполнение программы напоминает предыдущий этап, однако в этом случае текущий узел имеет номер 2837, а значение следующего узла равно 4499. Проверка в строке 256 возвращает TRUE, и новый узел вставляется между двумя существующими.
Когда пользователь вводит 0, условное выражение в строке 279 возвращает TRUE и цикл while(1) прерывается. В строке 300 функция-член Display() присваивается указателю на функции-члены pFunc. В профессиональной программе присвоение должно проходить динамически, основываясь на выборе пользователем.
Указатель функции-члена передается методу Iterate класса PartsList. В строке 208 метод Iterate() проверяет, не является ли список пустым. Затем в строках 213—215 последовательно с помощью указателя функции-члена вызываются из списка все объекты Part. В итоге для объекта Part вызывается соответствующий вариант метода Display(), в результате чего для разных объектов выводится разная информация.
