
- •Предисловие
- •Структура книги
- •Глава 1. Начинаем
- •1.1. Решение задачи
- •1.2. Программа на языке C++
- •1.2.1. Порядок выполнения инструкций
- •1.3. Директивы препроцессора
- •1.4. Немного о комментариях
- •1.5. Первый взгляд на ввод/вывод
- •1.5.1. Файловый ввод/вывод
- •Глава 2. Краткий обзор С++
- •2.1. Встроенный тип данных “массив”
- •2.2. Динамическое выделение памяти и указатели
- •2.3. Объектный подход
- •2.4. Объектно-ориентированный подход
- •2.5. Использование шаблонов
- •2.6. Использование исключений
- •2.7. Использование пространства имен
- •2.8. Стандартный массив – это вектор
- •Глава 3. Типы данных С++
- •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.7. Тип bool
- •3.8. Перечисления
- •3.9. Тип “массив”
- •3.9.1. Многомерные массивы
- •3.9.2. Взаимосвязь массивов и указателей
- •3.10. Класс vector
- •3.13. Спецификатор volatile
- •3.14. Класс pair
- •3.15. Типы классов
- •Глава 4. Выражения
- •4.1. Что такое выражение?
- •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.2. Поиск и извлечение элемента отображения
- •6.12.3. Навигация по элементам отображения
- •6.12.4. Словарь
- •6.12.5. Удаление элементов map
- •6.13. Построение набора стоп-слов
- •6.13.2. Поиск элемента
- •6.13.3. Навигация по множеству
- •6.14. Окончательная программа
- •6.15. Контейнеры multimap и multiset
- •6.16. Стек
- •6.17. Очередь и очередь с приоритетами
- •6.18. Вернемся в классу iStack
- •Глава 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.5. Значения параметров по умолчанию
- •7.3.6. Многоточие
- •7.4. Возврат значения
- •7.5. Рекурсия
- •7.6. Встроенные функции
- •7.8.1. Класс для обработки параметров командной строки
- •7.9. Указатели на функции
- •7.9.1. Тип указателя на функцию
- •7.9.2. Инициализация и присваивание
- •7.9.3. Вызов
- •7.9.4. Массивы указателей на функции
- •7.9.5. Параметры и тип возврата
- •Глава 8. Область видимости и время жизни
- •8.1. Область видимости
- •8.1.1. Локальная область видимости
- •8.2. Глобальные объекты и функции
- •8.2.1. Объявления и определения
- •8.2.2. Сопоставление объявлений в разных файлах
- •8.2.3. Несколько слов о заголовочных файлах
- •8.3.1. Автоматические объекты
- •8.3.2. Регистровые автоматические объекты
- •8.3.3. Статические локальные объекты
- •8.4. Динамически размещаемые объекты
- •8.4.3. Динамическое создание и уничтожение массивов
- •8.5.1. Определения пространства имен
- •8.5.2. Оператор разрешения области видимости
- •8.5.3. Вложенные пространства имен
- •8.5.4. Определение члена пространства имен
- •8.5.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.2. Три шага разрешения перегрузки
- •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.5.1. Модель компиляции с включением
- •10.5.2. Модель компиляции с разделением
- •10.5.3. Явные объявления конкретизации
- •10.11. Пример шаблона функции
- •Глава 11. Обработка исключений
- •11.1. Возбуждение исключения
- •11.2. try-блок
- •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.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.1. Алгоритмы поиска
- •12.5.2. Алгоритмы сортировки и упорядочения
- •12.5.3. Алгоритмы удаления и подстановки
- •12.5.4. Алгоритмы перестановки
- •12.5.5. Численные алгоритмы
- •12.5.6. Алгоритмы генерирования и модификации
- •12.5.8. Алгоритмы работы с множествами
- •12.5.9. Алгоритмы работы с хипом
- •12.6.1. Операция list_merge()
- •12.6.2. Операция list::remove()
- •12.6.3. Операция list::remove_if()
- •12.6.5. Операция list::sort()
- •12.6.6. Операция list::splice()
- •12.6.7. Операция list::unique()
- •Глава 13. Классы
- •13.1. Определение класса
- •13.1.1. Данные-члены
- •13.1.2. Функции-члены
- •13.1.3. Доступ к членам
- •13.1.4. Друзья
- •13.1.5. Объявление и определение класса
- •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.1. Разрешение имен в области видимости класса
- •Глава 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.2. Вектор объектов
- •14.5. Список инициализации членов
- •14.6.1. Инициализация члена, являющегося объектом класса
- •Глава 15. Перегруженные операторы и определенные пользователем преобразования
- •15.1. Перегрузка операторов
- •15.1.1. Члены и не члены класса
- •15.1.2. Имена перегруженных операторов
- •15.1.3. Разработка перегруженных операторов
- •15.2. Друзья
- •15.3. Оператор =
- •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.1. Еще раз о разрешении перегрузки функций
- •15.10.2. Функции-кандидаты
- •15.11.1. Объявления перегруженных функций-членов
- •15.11.2. Функции-кандидаты
- •15.11.3. Устоявшие функции
- •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.2. Модель компиляции с разделением
- •16.8.3. Явные объявления конкретизации
- •16.12. Пространства имен и шаблоны классов
- •16.13. Шаблон класса Array
- •Глава 17. Наследование и подтипизация классов
- •17.1. Определение иерархии классов
- •17.1.1. Объектно-ориентированное проектирование
- •17.2. Идентификация членов иерархии
- •17.2.1. Определение базового класса
- •17.2.3. Резюме
- •17.3. Доступ к членам базового класса
- •17.4. Конструирование базового и производного классов
- •17.4.1. Конструктор базового класса
- •17.4.2. Конструктор производного класса
- •17.4.3. Альтернативная иерархия классов
- •17.4.4. Отложенное обнаружение ошибок
- •17.4.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.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.5.1. Объявление виртуального базового класса
- •18.5.2. Специальная семантика инициализации
- •18.5.3. Порядок вызова конструкторов и деструкторов
- •18.5.4. Видимость членов виртуального базового класса
- •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.4. Объекты-исключения и виртуальные функции
- •19.2.5. Раскрутка стека и вызов деструкторов
- •19.2.6. Спецификации исключений
- •19.2.7. Конструкторы и функциональные try-блоки
- •19.3.1. Функции-кандидаты
- •19.3.3. Наилучшая из устоявших функций
- •Глава 20. Библиотека IOSTREAM
- •20.1. Оператор вывода <<
- •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_if()
- •equal()
- •equal_range()
- •fill()
- •find()
- •find_if()
- •find_end()
- •find_first_of()
- •generate()
- •generate_n()
- •includes()
- •inplace_merge()
- •iter_swap()
- •lexicographical_compare()
- •max_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_copy()
- •rotate()
- •search_n()
- •set_intersection()
- •set_union()
- •sort()
- •stable_partition()
- •swap()
- •swap_ranges()
- •transform()
- •unique_copy()
- •upper_bound()
- •Алгоритмы для работы с хипом
- •make_heap()
- •pop_heap()
- •push_heap()
- •sort_heap()

С++ для начинающих |
587 |
int array1[ 10 ] = { 34, 0, 8, 3, 1, 13, 2, 5, 21, 1 }; int array2[ 5 ] = { 377, 89, 233, 55, 144 };
list< int > ilist1( array1, array1 + 10 ); list< int > ilist2( array2, array2 + 5 );
// для объединения требуется, чтобы оба списка были упорядочены ilist1.sort(); ilist2.sort();
ilist1.merge( ilist2 );
После выполнения операции merge() список ilist2 пуст, а ilist1 содержит первые 15 чисел Фибоначчи в порядке возрастания.
12.6.2. Операция list::remove()
void list::remove( const elemType &value );
Операция remove() удаляет все элементы с заданным значением:
ilist1.remove( 1 );
template < class Predicate >
12.6.3. Операция list::remove_if()
void list::remove_if( Predicate pred );
Операция remove_if() удаляет все элементы, для которых выполняется указанное
class Even { public:
bool operator()( int elem ) { return ! (elem % 2 ); }
};
условие, т.е. предикат pred возвращает true. Например:
ilist1.remove_if( Even() );
удаляет все четные числа из списка, определенного при рассмотрении merge().
12.6.4. Операция list::reverse()
void list::reverse();

С++ для начинающих |
588 |
Операция reverse() изменяет порядок следования элементов списка на противоположный:
ilist1.reverse();
void list::sort(); template <class Compare>
12.6.5. Операция list::sort()
void list::sort( Compare comp );
По умолчанию sort() упорядочивает элементы списка по возрастанию с помощью оператора “меньше”, определенного в классе элементов контейнера. Вместо этого можно явно передать в качестве аргумента оператор сравнения. Так,
list1.sort();
упорядочивает list1 по возрастанию, а
list1.sort( greater<int>() );
упорядочивает list1 по убыванию, используя оператор “больше”.
void list::splice( iterator pos, list rhs );
void list::splice( iterator pos, list rhs, iterator ix ); void list::splice( iterator pos, list rhs,
12.6.6. Операция list::splice()
iterator first, iterator last );
Операция splice() имеет три формы: перемещение одного элемента, всех элементов или диапазона из одного списка в другой. В каждом случае передается итератор, указывающий на позицию вставки, а перемещаемые элементы располагаются
int array[ 10 ] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 }; list< int > ilist1( array, array + 10 );
непосредственно перед ней. Если даны два списка:
list< int > ilist2( array, array + 2 ); // содержит 0, 1
то следующее обращение к splice() перемещает первый элемент ilist1 в ilist2. Теперь ilist2 содержит элементы 0, 1 и 0, тогда как в ilist1 элемента 0 больше нет.

С++ для начинающих |
589 |
//ilist2.end() указывает на позицию, куда нужно переместить элемент
//элементы вставляются перед этой позицией
//ilist1 указывает на список, из которого перемещается элемент
//ilist1.begin() указывает на сам перемещаемый элемент
ilis2.splice( ilist2.end(), ilist1, ilist1.begin() );
В следующем примере применения splice() передаются два итератора,
list< int >::iterator first, last;
first = ilist1.find( 2 ); last = ilist1.find( 13 );
ограничивающие диапазон перемещаемых элементов:
ilist2.splice( ilist2.begin(), ilist1, first, last );
В данном случае элементы 2, 3, 5 и 8 удаляются из ilist1 и вставляются в начало ilist2. Теперь ilist1 содержит пять элементов 1, 1, 13, 21 и 34. Для их перемещения
list< int >::iterator pos = ilist2.find( 5 );
в ilist2 можно воспользоваться третьей вариацией операции splice():
ilist2.splice( pos, ilist1 );
Итак, список ilist1 пуст. Последние пять элементов перемещены в позицию списка ilist2, предшествующую той, которую занимает элемент 5.
void list::unique();
template <class BinaryPredicate>
12.6.7. Операция list::unique()
void list::unique( BinaryPredicate pred );
Операция unique() удаляет соседние дубликаты. По умолчанию при сравнении используется оператор равенства, определенный для типа элементов контейнера. Например, если даны значения {0,2,4,6,4,2,0}, то после применения unique() список останется таким же, поскольку в соседних позициях дубликатов нет. Но если мы сначала отсортируем список, что даст {0,0,2,2,4,4,6}, а потом применим unique(), то получим четыре различных значения {0,2,4,6}.
ilist.unique();
Вторая форма unique() принимает альтернативный оператор сравнения. Например,

С++ для начинающих |
590 |
class EvenPair { public:
bool operator()( int val1, val2 ) { return ! (val2 % val1 ); }
};
ilist.unique( EvenPair() );
удаляет соседние элементы, если второй элемент без остатка делится на первый.
Эти операции, являющиеся членами класса, следует предпочесть соответствующим обобщенным алгоритмам при работе со списками. Остальные обобщенные алгоритмы, такие, как find(), transform(), for_each() и т.д., работают со списками так же эффективно, как и с другими контейнерами (еще раз напомним, что подробно все алгоритмы рассматриваются в Приложении).
Упражнение 12.8 Измените программу из раздела 12.2, используя список вместо вектора.
С++ для начинающих |
591 |
Часть IV
Объектное программирование
Вчасти 4 мы сосредоточимся на объектном программировании, т.е. на применении классов C++ для определения новых типов, манипулировать которыми так же просто, как и встроенными. Создавая новые типы для описания предметной области, C++ помогает программисту писать более легкие для понимания приложения. Классы позволяют отделить детали, касающиеся реализации нового типа, от определения интерфейса и операций, предоставляемых пользователю. При этом уделяется меньше внимания мелочам, из-за чего программирование становится таким утомительным занятием. Значимые для приложения типы можно реализовать всего один раз, после чего использовать повторно. Средства, обеспечивающие инкапсуляцию данных и функций, необходимых для реализации типа, помогают значительно упростить последующее сопровождение и развитие приложения.
Вглаве 13 мы рассмотрим общий механизм классов: порядок их определения, концепцию сокрытия информации (т.е. отделение открытого интерфейса от закрытой реализации), способы определения и манипулирования объектами класса, область видимости, вложенные классы и классы как члены пространства имен.
Вглаве 14 изучаются предоставляемые C++ средства инициализации и уничтожения объектов класса, а также присваивания им значений путем применения таких специальных функций-членов класса, как конструкторы, деструкторы и копирующие конструкторы. Мы рассмотрим вопрос о почленной инициализации и копировании, когда объект класса инициализируется или ему присваивается значение другого объекта того же класса.
Вглаве 15 мы расскажем о перегрузке операторов, которая позволяет использовать операнды типа класса со встроенными операторами, описанными в главе 4. Таким образом, работа с объектами типа класса может быть сделана столь же понятной, как и работа со встроенными типами. В начале главы 15 представлены общие концепции и соображения, касающиеся проектирования перегрузки операторов, а затем рассмотрены конкретные операторы, такие, как присваивание, взятие индекса, вызов, а также специфичные для классов операторы new и delete. Иногда необходимо объявить
перегруженный оператор, как друга класса, наделив его специальными правами доступа, в данной главе объясняется, зачем это нужно. Здесь же представлен еще один специальный вид функций-членов – конвертеры, которые позволяют программисту определить стандартные преобразования. Конвертеры неявно применяются компилятором, когда объекты класса используются в качестве фактических аргументов функции или операндов встроенного либо перегруженного оператора. Завершается глава изложением правил разрешения перегрузки функций с учетом аргументов типа класса, функций-членов и перегруженных операторов.
Тема главы 16 – шаблоны классов. Шаблон – это предписание для создания класса, в котором один или несколько типов параметризованы. Например, vector может быть параметризован типом элементов, хранящихся в нем, а buffer – типом элементов в буфере или его размером. В этой главе объясняется, как определить и конкретизировать шаблон. Поддержка классов в C++ теперь рассматривается иначе – в свете наличия шаблонов, и снова обсуждаются функции-члены, объявления друзей и вложенные типы. Здесь мы еще раз вернемся к модели компиляции шаблонов, описанной в главе 10, чтобы показать, какое влияние оказывают на нее шаблоны классов.