- •Часть I
- •1. Начинаем
- •1.1. Решение задачи
- •1.2.1. Порядок выполнения инструкций
- •1.3. Директивы препроцессора
- •1.4. Немного о комментариях
- •1.5. Первый взгляд на ввод/вывод
- •1.5.1. Файловый ввод/вывод
- •2.1. Встроенный тип данных “массив”
- •2.2. Динамическое выделение памяти и указатели
- •2.3. Объектный подход
- •2.4. Объектно-ориентированный подход
- •2.5. Использование шаблонов
- •2.6. Использование исключений
- •2.7. Использование пространства имен
- •2.8. Стандартный массив – это вектор
- •Часть II
- •3.1. Литералы
- •3.2. Переменные
- •3.2.1. Что такое переменная
- •3.2.2. Имя переменной
- •3.2.3. Определение объекта
- •3.3. Указатели
- •3.4. Строковые типы
- •3.4.1. Встроенный строковый тип
- •3.4.2. Класс string
- •3.5. Спецификатор const
- •3.6. Ссылочный тип
- •3.8. Перечисления
- •3.9. Тип “массив”
- •3.9.1. Многомерные массивы
- •3.9.2. Взаимосвязь массивов и указателей
- •3.10. Класс vector
- •3.11. Класс complex
- •3.12. Директива typedef
- •3.13. Спецификатор volatile
- •3.14. Класс pair
- •3.15. Типы классов
- •4. Выражения
- •4.1. Что такое выражение?
- •4.2. Арифметические операции
- •4.3. Операции сравнения и логические операции
- •4.4. Операции присваивания
- •4.5. Операции инкремента и декремента
- •4.6. Операции с комплексными числами
- •4.7. Условное выражение
- •4.8. Оператор sizeof
- •4.9. Операторы new и delete
- •4.10. Оператор “запятая”
- •4.11. Побитовые операторы
- •4.12. Класс bitset
- •4.13. Приоритеты
- •4.14. Преобразования типов
- •4.14.1. Неявное преобразование типов
- •4.14.2. Арифметические преобразования типов
- •4.14.3. Явное преобразование типов
- •4.14.4. Устаревшая форма явного преобразования
- •4.15. Пример: реализация класса Stack
- •5. Инструкции
- •5.1. Простые и составные инструкции
- •5.2. Инструкции объявления
- •5.3. Инструкция if
- •5.4. Инструкция switch
- •5.5. Инструкция цикла for
- •5.6. Инструкция while
- •5.8. Инструкция do while
- •5.8. Инструкция break
- •5.9. Инструкция continue
- •5.10. Инструкция goto
- •5.11. Пример связанного списка
- •5.11.1. Обобщенный список
- •6. Абстрактные контейнерные типы
- •6.1. Система текстового поиска
- •6.2. Вектор или список?
- •6.3. Как растет вектор?
- •6.4. Как определить последовательный контейнер?
- •6.5. Итераторы
- •6.6. Операции с последовательными контейнерами
- •6.6.1. Удаление
- •6.6.2. Присваивание и обмен
- •6.6.3. Обобщенные алгоритмы
- •6.7. Читаем текстовый файл
- •6.8. Выделяем слова в строке
- •6.9. Обрабатываем знаки препинания
- •6.10. Приводим слова к стандартной форме
- •6.11. Дополнительные операции со строками
- •6.12. Строим отображение позиций слов
- •6.12.1. Определение объекта map и заполнение его элементами
- •6.12.2. Поиск и извлечение элемента отображения
- •6.12.3. Навигация по элементам отображения
- •6.12.4. Словарь
- •6.12.5. Удаление элементов map
- •6.13. Построение набора стоп-слов
- •6.13.1. Определение объекта set и заполнение его элементами
- •6.13.2. Поиск элемента
- •6.13.3. Навигация по множеству
- •6.14. Окончательная программа
- •6.15. Контейнеры multimap и multiset
- •6.16. Стек
- •6.17. Очередь и очередь с приоритетами
- •6.18. Вернемся в классу iStack
- •Часть III
- •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.3.4. Абстрактные контейнерные типы в качестве параметров
- •7.3.5. Значения параметров по умолчанию
- •7.3.6. Многоточие
- •7.4. Возврат значения
- •7.4.1. Передача данных через параметры и через глобальные объекты
- •7.5. Рекурсия
- •7.6. Встроенные функции
- •7.7. Директива связывания extern "c" a
- •7.8. Функция main(): разбор параметров командной строки
- •7.8.1. Класс для обработки параметров командной строки
- •7.9. Указатели на функции
- •7.9.1. Тип указателя на функцию
- •7.9.2. Инициализация и присваивание
- •7.9.3. Вызов
- •7.9.4. Массивы указателей на функции
- •7.9.5. Параметры и тип возврата
- •7.9.6. Указатели на функции, объявленные как extern "c"
- •8. Область видимости и время жизни
- •8.1. Область видимости
- •8.1.1. Локальная область видимости
- •8.2. Глобальные объекты и функции
- •8.2.1. Объявления и определения
- •8.2.2. Сопоставление объявлений в разных файлах
- •8.2.3. Несколько слов о заголовочных файлах
- •8.3. Локальные объекты
- •8.3.1. Автоматические объекты
- •8.3.2. Регистровые автоматические объекты
- •8.3.3. Статические локальные объекты
- •8.4. Динамически размещаемые объекты
- •8.4.1. Динамическое создание и уничтожение единичных объектов
- •8.4.2. Шаблон auto_ptr а
- •8.4.3. Динамическое создание и уничтожение массивов
- •8.4.4. Динамическое создание и уничтожение константных объектов
- •8.4.5. Оператор размещения new а
- •8.5. Определения пространства имен а
- •8.5.1. Определения пространства имен
- •8.5.2. Оператор разрешения области видимости
- •8.5.3. Вложенные пространства имен
- •8.5.4. Определение члена пространства имен
- •8.5.5. Поо и члены пространства имен
- •8.5.6. Безымянные пространства имен
- •8.6. Использование членов пространства имен а
- •8.6.1. Псевдонимы пространства имен
- •8.6.2. Using-объявления
- •8.6.3. Using-директивы
- •8.6.4. Стандартное пространство имен std
- •9. Перегруженные функции
- •9.1. Объявления перегруженных функций
- •9.1.1. Зачем нужно перегружать имя функции
- •9.1.2. Как перегрузить имя функции
- •9.1.3. Когда не надо перегружать имя функции
- •9.1.4. Перегрузка и область видимости a
- •9.1.5. Директива extern "c" и перегруженные функции a
- •9.1.6. Указатели на перегруженные функции a
- •9.1.7. Безопасное связывание a
- •9.2. Три шага разрешения перегрузки
- •9.3. Преобразования типов аргументов a
- •9.3.1. Подробнее о точном соответствии
- •9.3.2. Подробнее о расширении типов
- •9.3.3. Подробнее о стандартном преобразовании
- •9.3.4. Ссылки
- •9.4. Детали разрешения перегрузки функций
- •9.4.1. Функции-кандидаты
- •9.4.2. Устоявшие функции
- •9.4.3. Наилучшая из устоявших функция
- •9.4.4. Аргументы со значениями по умолчанию
- •10. Шаблоны функций
- •10.1. Определение шаблона функции
- •10.2. Конкретизация шаблона функции
- •10.3. Вывод аргументов шаблона а
- •10.4. Явное задание аргументов шаблона a
- •10.5. Модели компиляции шаблонов а
- •10.5.1. Модель компиляции с включением
- •10.5.2. Модель компиляции с разделением
- •10.5.3. Явные объявления конкретизации
- •10.6. Явная специализация шаблона а
- •10.7. Перегрузка шаблонов функций а
- •10.8. Разрешение перегрузки при конкретизации a
- •10.9. Разрешение имен в определениях шаблонов а
- •10.10. Пространства имен и шаблоны функций а
- •10.11. Пример шаблона функции
- •11. Обработка исключений
- •11.1. Возбуждение исключения
- •11.3. Перехват исключений
- •11.3.1. Объекты-исключения
- •11.3.2. Раскрутка стека
- •11.3.3. Повторное возбуждение исключения
- •11.3.4. Перехват всех исключений
- •11.4. Спецификации исключений
- •11.4.1. Спецификации исключений и указатели на функции
- •11.5. Исключения и вопросы проектирования
- •12. Обобщенные алгоритмы
- •12.1. Краткий обзор
- •12.2. Использование обобщенных алгоритмов
- •12.3. Объекты-функции
- •12.3.1. Предопределенные объекты-функции
- •12.3.2. Арифметические объекты-функции
- •12.3.3. Сравнительные объекты-функции
- •12.3.4. Логические объекты-функции
- •12.3.5. Адаптеры функций для объектов-функций
- •12.3.6. Реализация объекта-функции
- •12.4. Еще раз об итераторах
- •12.4.1. Итераторы вставки
- •12.4.2. Обратные итераторы
- •12.4.3. Потоковые итераторы
- •12.4.4. Итератор istream_iterator
- •12.4.5. Итератор ostream_iterator
- •12.4.6. Пять категорий итераторов
- •12.5. Обобщенные алгоритмы
- •12.5.1. Алгоритмы поиска
- •12.5.2. Алгоритмы сортировки и упорядочения
- •12.5.3. Алгоритмы удаления и подстановки
- •12.5.4. Алгоритмы перестановки
- •12.5.9. Алгоритмы работы с хипом
- •12.6. Когда нельзя использовать обобщенные алгоритмы
- •12.6.1. Операция list_merge()
- •12.6.2. Операция list::remove()
- •12.6.3. Операция list::remove_if()
- •12.6.4. Операция list::reverse()
- •12.6.5. Операция list::sort()
- •12.6.6. Операция list::splice()
- •12.6.7. Операция list::unique()
- •Часть IV
- •13. Классы
- •13.1. Определение класса
- •13.1.1. Данные-члены
- •13.1.2. Функции-члены
- •13.1.3. Доступ к членам
- •13.1.4. Друзья
- •13.1.5. Объявление и определение класса
- •13.2. Объекты классов
- •13.3. Функции-члены класса
- •13.3.1. Когда использовать встроенные функции-члены
- •13.3.2. Доступ к членам класса
- •13.3.3. Закрытые и открытые функции-члены
- •13.3.4. Специальные функции-члены
- •13.3.5. Функции-члены со спецификаторами const и volatile
- •13.3.6. Объявление mutable
- •13.4. Неявный указатель this
- •13.4.1. Когда использовать указатель this
- •13.5. Статические члены класса
- •13.5.1. Статические функции-члены
- •13.6. Указатель на член класса
- •13.6.1. Тип члена класса
- •13.6.2. Работа с указателями на члены класса
- •13.6.3. Указатели на статические члены класса
- •13.7. Объединение – класс, экономящий память
- •13.8. Битовое поле – член, экономящий память
- •13.9. Область видимости класса a
- •13.9.1. Разрешение имен в области видимости класса
- •13.10. Вложенные классы a
- •13.10.1. Разрешение имен в области видимости вложенного класса
- •13.11. Классы как члены пространства имен a
- •13.12. Локальные классы a
- •14. Инициализация, присваивание и уничтожение класса
- •14.1. Инициализация класса
- •14.2. Конструктор класса
- •14.2.1. Конструктор по умолчанию
- •14.2.2. Ограничение прав на создание объекта
- •14.2.3. Копирующий конструктор
- •14.3. Деструктор класса
- •14.3.1. Явный вызов деструктора
- •14.3.2. Опасность увеличения размера программы
- •14.4. Массивы и векторы объектов
- •14.4.1. Инициализация массива, распределенного из хипа a
- •14.4.2. Вектор объектов
- •14.5. Список инициализации членов
- •14.6. Почленная инициализация a
- •14.6.1. Инициализация члена, являющегося объектом класса
- •14.7. Почленное присваивание a
- •14.8. Соображения эффективности a
- •15. Перегруженные операторы и определенные пользователем преобразования
- •15.1. Перегрузка операторов
- •15.1.1. Члены и не члены класса
- •15.1.2. Имена перегруженных операторов
- •15.1.3. Разработка перегруженных операторов
- •15.2. Друзья
- •15.4. Оператор взятия индекса
- •15.5. Оператор вызова функции
- •15.6. Оператор “стрелка”
- •15.7. Операторы инкремента и декремента
- •15.8. Операторы new и delete
- •15.8.1. Операторы new[ ] и delete [ ]
- •15.8.2. Оператор размещения new() и оператор delete()
- •15.9. Определенные пользователем преобразования
- •15.9.1. Конвертеры
- •15.9.2. Конструктор как конвертер
- •15.10. Выбор преобразования a
- •15.10.1. Еще раз о разрешении перегрузки функций
- •15.10.2. Функции-кандидаты
- •15.10.3. Функции-кандидаты для вызова функции в области видимости класса
- •15.10.4. Ранжирование последовательностей определенных пользователем преобразований
- •15.11. Разрешение перегрузки и функции-члены a
- •15.11.1. Объявления перегруженных функций-членов
- •15.11.2. Функции-кандидаты
- •15.11.3. Устоявшие функции
- •15.12. Разрешение перегрузки и операторы a
- •15.12.1. Операторные функции-кандидаты
- •15.12.2. Устоявшие функции
- •15.12.3. Неоднозначность
- •16. Шаблоны классов
- •16.1. Определение шаблона класса
- •16.1.1. Определения шаблонов классов Queue и QueueItem
- •16.2. Конкретизация шаблона класса
- •16.2.1. Аргументы шаблона для параметров-констант
- •16.3. Функции-члены шаблонов классов
- •16.3.1. Функции-члены шаблонов Queue и QueueItem
- •16.4. Объявления друзей в шаблонах классов
- •16.4.1. Объявления друзей в шаблонах Queue и QueueItem
- •16.5. Статические члены шаблонов класса
- •16.6. Вложенные типы шаблонов классов
- •16.7. Шаблоны-члены
- •16.8. Шаблоны классов и модель компиляции a
- •16.8.1. Модель компиляции с включением
- •16.8.2. Модель компиляции с разделением
- •16.8.3. Явные объявления конкретизации
- •16.9. Специализации шаблонов классов a
- •16.10. Частичные специализации шаблонов классов a
- •16.11. Разрешение имен в шаблонах классов a
- •16.12. Пространства имен и шаблоны классов
- •16.13. Шаблон класса Array
- •Часть V
- •17. Наследование и подтипизация классов
- •17.1. Определение иерархии классов
- •17.1.1. Объектно-ориентированное проектирование
- •17.2. Идентификация членов иерархии
- •17.2.1. Определение базового класса
- •17.2.2. Определение производных классов
- •17.2.3. Резюме
- •17.3. Доступ к членам базового класса
- •17.4. Конструирование базового и производного классов
- •17.4.1. Конструктор базового класса
- •17.4.2. Конструктор производного класса
- •17.4.3. Альтернативная иерархия классов
- •17.4.4. Отложенное обнаружение ошибок
- •17.4.5. Деструкторы
- •17.5. Виртуальные функции в базовом и производном классах
- •17.5.1. Виртуальный ввод/вывод
- •17.5.2. Чисто виртуальные функции
- •17.5.3. Статический вызов виртуальной функции
- •17.5.4. Виртуальные функции и аргументы по умолчанию
- •17.5.5. Виртуальные деструкторы
- •17.5.6. Виртуальная функция eval()
- •17.5.7. Почти виртуальный оператор new
- •17.5.8. Виртуальные функции, конструкторы и деструкторы
- •17.6. Почленная инициализация и присваивание a
- •17.7. Управляющий класс UserQuery
- •17.7.1. Определение класса UserQuery
- •17.8. Соберем все вместе
- •18. Множественное и виртуальное наследование
- •18.1. Готовим сцену
- •18.2. Множественное наследование
- •18.3. Открытое, закрытое и защищенное наследование
- •18.3.1. Наследование и композиция
- •18.3.2. Открытие отдельных членов
- •18.3.3. Защищенное наследование
- •18.3.4. Композиция объектов
- •18.4. Область видимости класса и наследование
- •18.4.1. Область видимости класса при множественном наследовании
- •18.5. Виртуальное наследование a
- •18.5.1. Объявление виртуального базового класса
- •18.5.2. Специальная семантика инициализации
- •18.5.3. Порядок вызова конструкторов и деструкторов
- •18.5.4. Видимость членов виртуального базового класса
- •18.6. Пример множественного виртуального наследования a
- •18.6.1. Порождение класса, контролирующего выход за границы массива
- •18.6.2. Порождение класса отсортированного массива
- •18.6.3. Класс массива с множественным наследованием
- •19.1. Идентификация типов во время выполнения
- •19.1.1. Оператор dynamic_cast
- •19.1.2. Оператор typeid
- •19.1.3. Класс type_info
- •19.2. Исключения и наследование
- •19.2.1. Исключения, определенные как иерархии классов
- •19.2.2. Возбуждение исключения типа класса
- •19.2.3. Обработка исключения типа класса
- •19.2.4. Объекты-исключения и виртуальные функции
- •19.2.5. Раскрутка стека и вызов деструкторов
- •19.2.6. Спецификации исключений
- •19.2.7. Конструкторы и функциональные try-блоки
- •19.3. Разрешение перегрузки и наследование a
- •19.3.1. Функции-кандидаты
- •19.3.2. Устоявшие функции и последовательности пользовательских преобразований
- •19.3.3. Наилучшая из устоявших функций
- •20. Библиотека iostream
- •20.2. Ввод
- •20.2.1. Строковый ввод
- •20.3. Дополнительные операторы ввода/вывода
- •20.4. Перегрузка оператора вывода
- •20.5. Перегрузка оператора ввода
- •20.6. Файловый ввод/вывод
- •20.7. Состояния потока
- •20.8. Строковые потоки
- •20.9. Состояние формата
- •20.10. Сильно типизированная библиотека
- •21. Обобщенные алгоритмы в алфавитном порядке
- •Алгоритм accumulate()
- •Алгоритм adjacent_difference()
- •Алгоритм adjacent_find()
- •Алгоритм binary_search()
- •Алгоритм copy()
- •Алгоритм copy_backward()
- •Алгоритм count()
- •Алгоритм count_if()
- •Алгоритм equal()
- •Алгоритм equal_range()
- •Алгоритм fill()
- •Алгоритм fill_n()
- •Алгоритм find()
- •Алгоритм find_if()
- •Алгоритм find_end()
- •Алгоритм find_first_of()
- •Алгоритм for_each()
- •Алгоритм generate()
- •Алгоритм generate_n()
- •Алгоритм includes()
- •Алгоритм inner_product()
- •Алгоритм inplace_merge()
- •Алгоритм iter_swap()
- •Алгоритм lexicographical_compare()
- •Алгоритм lower_bound()
- •Алгоритм max()
- •Алгоритм max_element()
- •Алгоритм min()
- •Алгоритм min_element()
- •Алгоритм merge()
- •Алгоритм mismatch()
- •Алгоритм next_permutation()
- •Алгоритм nth_element()
- •Алгоритм partial_sort()
- •Алгоритм partial_sort_copy()
- •Алгоритм partial_sum()
- •Алгоритм partition()
- •Алгоритм prev_permutation()
- •Алгоритм random_shuffle()
- •Алгоритм remove()
- •Алгоритм remove_copy()
- •Алгоритм remove_if()
- •Алгоритм remove_copy_if()
- •Алгоритм replace()
- •Алгоритм replace_copy()
- •Алгоритм replace_if()
- •Алгоритм replace_copy_if()
- •Алгоритм reverse()
- •Алгоритм reverse_copy()
- •Алгоритм rotate()
- •Алгоритм rotate_copy()
- •Алгоритм search()
- •Алгоритм search_n()
- •Алгоритм set_difference()
- •Алгоритм set_intersection()
- •Алгоритм set_symmetric_difference()
- •Алгоритм set_union()
- •Алгоритм sort()
- •Алгоритм stable_partition()
- •Алгоритм stable_sort()
- •Алгоритм swap()
- •Алгоритм swap_ranges()
- •Алгоритм transform()
- •Алгоритм unique()
- •Алгоритм unique_copy()
- •Алгоритм upper_bound()
- •Алгоритмы для работы с хипом
- •Алгоритм make_heap()
- •Алгоритм pop_heap()
- •Алгоритм push_heap()
- •Алгоритм sort_heap()
7.8. Функция main(): разбор параметров командной строки
При запуске программы мы, как правило, передаем ей информацию в командной строке. Например, можно написать
prog -d -o of lie dataO
Фактические параметры являются аргументами функции main() и могут быть получены из массива C-строк с именем argv; мы покажем, как их использовать.
Во всех предыдущих примерах определение main() содержало пустой список:
int main() { ... }
Развернутая сигнатура main() позволяет получить доступ к параметрам, которые были заданы пользователем в командной строке:
int main( int argc, char *argv[] ){...}
argc содержит их количество, а argv – C-строки, представляющие собой отдельные значения (в командной строке они разделяются пробелами). Скажем, при запуске команды
prog -d -o ofile data0
argc получает значение 5, а argv включает следующие строки:
argv[ 0 ] = "prog";
argv[ 1 ] = "-d";
argv[ 2 ] = "-o";
argv[ 3 ] = "ofile";
argv[ 4 ] = "dataO";
В argv[0] всегда входит имя команды (программы). Элементы с индексами от 1 до argc-1 служат параметрами.
Посмотрим, как можно извлечь и использовать значения, помещенные в argv. Пусть программа из нашего примера вызывается таким образом:
prog [-d] [-h] [-v]
[-o output_file] [-l limit_value]
file_name
[ file_name [file_name [ ... ]]]
Параметры в квадратных скобках являются необязательными. Вот, например, запуск программы с их минимальным количеством – одним лишь именем файла:
prog chap1.doc
Но можно запускать и так:
prog -l 1024 -o chap1-2.out chapl.doc chap2.doc
prog d chap3.doc
prog -l 512 -d chap4.doc
При разборе параметров командной строки выполняются следующие основные шаги:
По очереди извлечь каждый параметр из argv. Мы используем для этого цикл for с начальным индексом 1 (пропуская, таким образом, имя программы):
for ( int ix = 1; ix < argc; ++ix ) {
char *pchar = argv[ ix ];
// ...
}
Определить тип параметра. Если строка начинается с дефиса (-), это одна из опций { h, d, v, l, o}. В противном случае это может быть либо значение, ассоциированное с опцией (максимальный размер для -l, имя выходного файла для -o), либо имя входного файла. Чтобы определить, начинается ли строка с дефиса, используем инструкцию switch:
switch ( pchar[ 0 ] ) {
case '-': {
// -h, -d, -v, -l, -o
}
default: {
// обработаем максимальный размер для опции -1
// имя выходного файла для -o
// имена входных файлов ...
}
}
Реализуем обработку двух случаев пункта 2.
Если строка начинается с дефиса, мы используем switch по следующему символу для определения конкретной опции. Вот общая схема этой части программы:
case '-': {
switch( pchar[ 1 ] )
{
case 'd':
// обработка опции debug
break;
case 'v':
// обработка опции version
break;
case 'h':
// обработка опции help
break;
case 'o':
// приготовимся обработать выходной файл
break;
case 'l':
// приготовимся обработать макс.размер
break;
default:
// неопознанная опция:
// сообщить об ошибке и завершить выполнение
}
}
Опция -d задает необходимость отладки. Ее обработка заключается в присваивании переменной с объявлением
bool debug_on = false;
значения true:
case 'd':
debug_on = true;
break;
В нашу программу может входить код следующего вида:
if ( debug_on )
display_state_elements( obj );
Опция -v выводит номер версии программы и завершает исполнение:
case 'v':
cout << program_name << "::"
<< program_version << endl;
return 0;
Опция -h запрашивает информацию о синтаксисе запуска и завершает исполнение. Вывод сообщения и выход из программы выполняется функцией usage():
case 'h':
// break не нужен: usage() вызывает exit()
usage();
Опция -o сигнализирует о том, что следующая строка содержит имя выходного файла. Аналогично опция -l говорит, что за ней указан максимальный размер. Как нам обработать эти ситуации?
Если в строке параметра нет дефиса, возможны три варианта: параметр содержит имя выходного файла, максимальный размер или имя входного файла. Чтобы различать эти случаи, присвоим true переменным, отражающим внутреннее состояние:
// если ofi1e_on==true,
// следующий параметр - имя выходного файла
bool ofi1e_on = false;
// если ofi1e_on==true,
// следующий параметр - максимальный размер
bool limit_on = false;
Вот обработка опций -l и -o в нашей инструкции switch:
case 'l':
limit_on = true;
break;
case 'o':
ofile_on = true;
break;
Встретив строку, не начинающуюся с дефиса, мы с помощью переменных состояния можем узнать ее содержание:
// обработаем максимальный размер для опции -1
// имя выходного файла для -o
// имена входных файлов ...
default: {
// ofile_on включена, если -o встречалась
if ( ofile_on ) {
// обработаем имя выходного файла
// выключим ofile_on
}
else if ( limit_on ) { // если -l встречалась
// обработаем максимальный размер
// выключим limit_on
} else {
// обработаем имя входного файла
}
}
Если аргумент является именем выходного файла, сохраним это имя и выключим ofile_on:
if ( ofile_on ) {
ofile_on = false;
ofile = pchar;
}
Если аргумент задает максимальный размер, мы должны преобразовать строку встроенного типа в представляемое ею число. Сделаем это с помощью стандартной функции atoi(), которая принимает строку в качестве аргумента и возвращает int (также существует функция atof(), возвращающая double). Для использования atoi() включим заголовочный файл ctype.h. Нужно проверить, что значение максимального размера неотрицательно и выключить limit_on:
// int limit;
else
if ( limit_on ) {
limit_on = false;
limit = atoi( pchar );
if ( limit < 0 ) {
cerr << program_name << "::"
<< program_version << " : error: "
<< "negative value for limit.\n\n";
usage( -2 );
}
}
Если обе переменных состояния равны false, у нас есть имя входного файла. Сохраним его в векторе строк:
else
file_names.push_back( string( pchar ));
При обработке параметров командной строки важен способ реакции на неверные опции. Мы решили, что задание отрицательной величины в качестве максимального размера будет фатальной ошибкой. Это приемлемо или нет в зависимости от ситуации. Также можно распознать эту ситуацию как ошибочную, выдать предупреждение и использовать ноль или какое-либо другое значение по умолчанию.
Слабость нашей реализации становится понятной, если пользователь небрежно относится к пробелам, разделяющим параметры. Скажем, ни одна из следующих двух строк не будет обработана:
prog - d dataOl
prog -oout_file dataOl
(Оба случая мы оставим для упражнений в конце раздела.)
Вот полный текст нашей программы. (Мы добавили инструкции печати для трассировки выполнения.)
#include <iostream>
#include <string>
#include <vector>
#include <ctype.h>
const char *const program_name = "comline";
const char *const program_version = "version 0.01 (08/07/97)";
inline void usage( int exit_value = 0 )
{
// печатает отформатированное сообщение о порядке вызова
// и завершает программу с кодом exit_value ...
cerr << "порядок вызова:\n"
<< program_name << " "
<< "[-d] [-h] [-v] \n\t"
<< "[-o output_file] [-l limit] \n\t"
<< "file_name\n\t[file_name [file_name [ ... ]]]\n\n"
<< "где [] указывает на необязательность опции:\n\n\t"
<< "-h: справка.\n\t\t"
<< "печать этого сообщения и выход\n\n\t"
<< "-v: версия.\n\t\t"
<< "печать информации о версии программы и выход\n\n\t"
<< "-d: отладка.\n\t\t включает отладочную печать\n\n\t"
<< "-l limit\n\t\t"
<< "limit должен быть неотрицательным целым числом\n\n\t"
<< "-o ofile\n\t\t"
<< "файл, в который выводится результат\n\t\t"
<< "по умолчанию результат записывается на стандартный вывод\n\n"
<< "file_name\n\t\t"
<< "имя подлежащего обработке файла\n\t\t"
<< "должно быть задано хотя бы одно имя --\n\t\t"
<< "но максимальное число не ограничено\n\n"
<< "примеры:\n\t\t"
<< "$command chapter7.doc\n\t\t"
<< "$command -d -l 1024 -o test_7_8 "
<< "chapter7.doc chapter8.doc\n\n";
exit( exit_value );
}
int main( int argc, char* argv[] )
{
bool debug_on = false;
bool ofile_on = false;
bool limit_on = false;
int limit = -1;
string ofile;
vector<string> file_names;
cout << "демонстрация обработки параметров в командной строке:\n"
<< "argc: " << argc << endl;
for ( int ix = 1; ix < argc; ++ix )
{
cout << "argv[ " << ix << " ]: "
<< argv[ ix ] << endl;
char *pchar = argv[ ix ];
switch ( pchar[ 0 ] )
{
case '-':
{
cout << "встретился \'-\'\n";
switch( pchar[ 1 ] )
{
case 'd':
cout << "встретилась -d: "
<< "отладочная печать включена\n";
debug_on = true;
break;
case 'v':
cout << "встретилась -v: "
<< "выводится информация о версии\n";
cout << program_name
<< " :: "
<< program_version
<< endl;
return 0;
case 'h':
cout << "встретилась -h: "
<< "справка\n";
// break не нужен: usage() завершает программу
usage();
case 'o':
cout << "встретилась -o: выходной файл\n";
ofile_on = true;
break;
case 'l':
cout << "встретилась -l: "
<< "ограничение ресурса\n";
limit_on = true;
break;
default:
cerr << program_name
<< " : ошибка : "
<< "неопознанная опция: - "
<< pchar << "\n\n";
// break не нужен: usage() завершает программу
usage( -1 );
}
break;
}
default: // либо имя файла
cout << "default: параметр без дефиса: "
<< pchar << endl;
if ( ofile_on ) {
ofile_on = false;
ofile = pchar;
}
else
if ( limit_on ) {
limit_on = false;
limit = atoi( pchar );
if ( limit < 0 ) {
cerr << program_name
<< " : ошибка : "
<< "отрицательное значение limit.\n\n";
usage( -2 );
}
}
else file_names.push_back( string( pchar ));
break;
}
}
if ( file_names.empty() ) {
cerr << program_name
<< " : ошибка : "
<< "не задан ни один входной файл.\n\n";
usage( -3 );
}
if ( limit != -1 )
cout << "Заданное пользователем значение limit: "
<< limit << endl;
if ( ! ofile.empty() )
cout << "Заданный пользователем выходной файл: "
<< ofile << endl;
cout << (file_names.size() == 1 ? "Файл, " : "Файлы, ")
<< "подлежащий(е) обработке:\n";
for ( int inx = 0; inx < file_names.size(); ++inx )
cout << "\t" << file_names[ inx ] << endl;
}
a.out -d -l 1024 -o test_7_8 chapter7.doc chapters.doc
Вот трассировка обработки параметров командной строки:
демонстрация обработки параметров в командной строке:
argc: 8
argv[ 1 ]: -d
встретился '-'
встретилась -d: отладочная печать включена
argv[ 2 ]: -l
встретился '-'
встретилась -l: ограничение ресурса
argv[ 3 ]: 1024
default: параметр без дефиса: 1024
argv[ 4 ]: -o
встретился '-'
встретилась -o: выходной файл
argv[ 5 ]: test_7_8
default: параметр без дефиса: test_7_8
argv[ 6 ]: chapter7.doc
default: параметр без дефиса: chapter7.doc
argv[ 7 ]: chapter8.doc
default: параметр без дефиса: chapter8.doc
Заданное пользователем значение limit: 1024
Заданный пользователем выходной файл: test_7_8
Файлы, подлежащий(е) обработке:
chapter7.doc
chapter8.doc