- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •1. Основы языка 8
- •1.2.2 Простые типы данных 13
- •2. Работа с памятью 73
- •3. Основы объектно-ориентированного программирования 87
- •4.Обработка исключений 114
- •5. Шаблонные функции и классы. Библиотека стандартных шаблонов 130
- •6. Паттерны проектирования 159
- •7. Антипаттерны 211
- •9. Методы отладки и оптимизации кода 242
- •1. Основы языка
- •1.1.2 Понятие проекта
- •1.2 Простые типы данных
- •1.2.1 Понятие типа
- •1.2.2 Простые типы данных
- •1.2.3 Внутреннее представление простых типов
- •1.2.4 Ключевое слово typedef. Тип size_t
- •1.3 Константы и переменные
- •1.3.1 Литералы
- •1. Числовые константы:
- •2. Символьные константы:
- •1.3.2 Переменные
- •1.3.3 Описание переменных
- •1.4. Выражения. Преобразование типов
- •1.4.1 Операнды и операции
- •1.4.2 Приоритет операций
- •1.4.3 Преобразование типов
- •1.5 Ветвления и циклы
- •1.5.2 Циклы
- •1.6 Массивы, строки
- •1.6.1 Основные понятия
- •1.6.2 Встроенные массивы
- •1.6.3 Cтроки. Обработка строк с завершающим нулём
- •1.7 Указатели и ссылки. Связь указателей и массивов. Библиотека cstring
- •1.7.1 Понятия указателя и ссылки
- •1.7.2 Связь между массивами и указателями
- •1.7.3 Библиотека cstring
- •1.8 Использование типов vector и string
- •1.8.1 Шаблонный класс vector
- •1.8.2 Класс string
- •1.9 Структуры и объединения. Битовые поля
- •1.10.1 Понятие функции
- •1.10.2 Описание функции и прототип функции
- •1.11 Параметры функции. Способы передачи параметров
- •1.11.1 Параметры функции и глобальные переменные
- •1.11.2 Способы передачи параметров в функцию
- •1.11.3 Передача массивов в функцию
- •1.11.4 Параметры-константы
- •1.11.5 Значения параметров по умолчанию
- •1.12.1 Указатель на функцию
- •1.12.2 Функции с переменным числом параметров
- •1.12.3 Перегрузка функций
- •1.12.4 Встроенные (inline) функции
- •1.13 Рекурсивные функции
- •1.14 Пространства имён
- •1.15 Директивы препроцессора. Макросы
- •2. Работа с памятью
- •2.1 Управление выделением и освобождением памяти
- •2.1.1 Статическое и динамическое выделение памяти
- •2.1.2 Способы динамического выделения и освобождения памяти
- •2.2 Динамические структуры данных
- •2.2.1 Основные понятия
- •2.2.2 Примеры реализации динамических структур на основе указателей
- •3. Основы объектно-ориентированного программирования
- •3.1 Основные понятия ооп
- •3.2.1 Описание класса
- •3.2.2 Область видимости элементов класса. Инкапсуляция
- •3.2.3 Первые примеры
- •3.3. Конструкторы и деструкторы.
- •3.4 Указатель this
- •3.5 Перегрузка операций
- •3.6 Дружественные функции и классы
- •3.7 Статические элементы класса
- •3.8 Наследование и полиморфизм
- •3.8.1. Основные понятия
- •3.8.2 Одиночное наследование
- •3.8.3 Множественное наследование
- •3.8.4 Конструкторы и деструкторы классов-потомков
- •3.9. Полиморфизм при наследовании классов
- •3.9.1 Механизмы раннего и позднего связывания
- •3.9.2 Абстрактные классы
- •4.Обработка исключений
- •4.1 Основные понятия
- •4.2 Перехват исключений
- •4.3 Поиск обработчика исключений. Раскрутка стека.
- •4.4 Повторное возбуждение исключений
- •4.5 "Аппаратные" и "программные" исключения
- •4.6 Стандартные классы исключений
- •4.7 Спецификация исключений, возбуждаемых функцией
- •4.8 Исключения в конструкторах при наследовании
- •4.9. Исключения в деструкторах
- •5. Шаблонные функции и классы. Библиотека стандартных шаблонов
- •5.1 Шаблонные функции
- •5.2 Шаблонные классы
- •5.3 Специализация шаблонов
- •5.4 Шаблонные параметры шаблонов
- •5.5 Разработка шаблонных классов с настраиваемой функциональностью
- •5.6 Использование шаблонов для вычислений на этапе компиляции
- •5.7 Библиотека стандартных шаблонов (stl) – основные понятия
- •5.8 Последовательные контейнеры. Итераторы
- •5.9. Адаптеры контейнеров
- •5.10 Ассоциативные контейнеры
- •5.11 Алгоритмы
- •6. Паттерны проектирования
- •6.1 Порождающие шаблоны
- •6.2 Структурные шаблоны
- •6.3 Шаблоны поведения
- •6.4 Шаблон "фабричный метод" (Factory method)
- •6.5 Шаблон "одиночка" (Singleton)
- •6.6 Шаблон "итератор" (Iterator)
- •6.7 Шаблон "наблюдатель" (Observer)
- •6.8 Шаблон "пул объектов" (Object pool)
- •6.9 Шаблон "команда" (Command)
- •6. 10 Шаблон "посетитель" (Visitor)
- •6.11 Дополнительные задания
- •6.11.1 Шаблон Iterator
- •6.11.2 Шаблон Observer
- •6.11.3 Шаблоны Command и Observer
- •6.11.5 Шаблон Visitor
- •6.11.5 Разработка класса − контейнера
- •6.11.6 Оценка производительности кода
- •7. Антипаттерны
- •7.1 Программирование методом копирования и вставки (Copy-Paste Programming)
- •7.2 Спагетти-код (Spaghetti code)
- •7.3 Магические числа (Magic numbers)
- •7.4 Бездумное комментирование
- •7.5 Жесткое кодирование (Hard code)
- •7.6 Мягкое кодирование (Soft code)
- •7.7 Золотой молоток (Golden hammer)
- •7.8 Слепая вера (Blind faith)
- •7.9 Ненужная сложность (Accidental complexity)
- •7.10 Божественный объект (God Object)
- •7.11 Лодочный якорь (Boat anchor)
- •7.12 Поток лавы (Lava flow)
- •7.13 Изобретение велосипеда (Reinventing the wheel)
- •7.14 Программирование перебором (Programming by permutation)
- •8.1 Выведение типов
- •8.2 Списки инициализации
- •8.3 Улучшение процесса инициализации объектов
- •8.4 Цикл for по коллекции
- •8.5 Лямбда-функции
- •8.6 Константа нулевого указателя nullptr
- •8.7 "Умные" указатели
- •9. Методы отладки и оптимизации кода
- •9.1 Отладка кода
- •9.1.1 Основные этапы отладки
- •9.1.2 Инструменты и приёмы отладки
- •9.2 Оптимизация кода
- •9.2.1 Рекомендации по выполнению оптимизации
- •9.2.2 Методики оптимизации кода
- •Заключение
- •Библиографический список
4.7 Спецификация исключений, возбуждаемых функцией
При объявлении функции в языке С++ можно явным образом объявить, какие исключения могут быть сгенерированы из этой функции. Для этого используется ключевое слово throw. Например, можно явно объявить, что функция firstLine способна возбудить исключение типа FileNotOpenedException.
std::string firstLine(const std::string &fileName)
throw (FileNotOpenedException) {
тело функции
}
Важно отметить, что на этапе компиляции не будет проверяться, что функция firstLine способна генерировать исключения только типа FileNotOpenedException. Такая проверка производится непосредственно во время выполнения программы: если в функции возникает исключение, не указанное в спецификации, то вызовется функция unexpected() из стандартной библиотеки C++, которая по умолчанию вызывает функцию terminate() для аварийного завершения программы.
Проверка соответствия исключений спецификации только во время выполнения программы не даёт особых плюсов, но при этом замедляет время работу программы (для сравнения, в языке Java подобная проверка выполняется на этапе компиляции). Поэтому в настоящее время спецификация исключений с помощью ключевого слова throw в языке C++ объявлена устаревшей и не рекомендуется к использованию.
Вместо этого в языке C++ появилось новое ключевое слово noexcept, говорящее, что функция не будет выбрасывать никаких исключений. Например:
inline int sqr(int x) noexcept {
return x * x;
}
Зачем помечать функции как noexcept? Во-первых, это приведёт к уменьшению результирующего исполняемого файла, поскольку компилятор не будет вставлять код для обработки исключений (снижение размера может быть довольно значительным).
Во-вторых, в некоторых случаях это может привести к повышению производительности. Например, согласно стандарту C++11, стандартные алгоритмы и контейнеры не должны использовать конструктор перемещения, если он способен выбрасывать исключения. В результате, если вы даже реализовали в классе конструктор перемещения, но не указали ключевое слово noexcept, то вполне вероятно, что при работе с STL всё равно будет использоваться более медленный конструктор копирования (хотя это может зависеть от конкретного компилятора).
Примечание. Кроме ключевого слова noexcept, в C++ имеется ещё оператор noexcept − он проверяет (во время компиляции), что аргумент специфицирован как не бросающий исключение. В качестве аргумента обычно выступает имя функции. Например, если функция sqr у нас объявлена с ключевым словом noexcept, то следующий код поместит true в переменную res:
bool res = noexcept(sqr);
4.8 Исключения в конструкторах при наследовании
Иногда возникает необходимость в конструкторе производного класса поймать исключение, возникшее в конструкторе родителя (на практике это требуется редко, но всё же иногда может понадобиться). Обычным образом это сделать нельзя. Однако, в C++ имеется специальный синтаксис, позволяющий решить эту задачу. Рассмотрим его на примере. Пусть в конструкторе базового класса A может возникнуть исключение типа std::invalid_argument. В конструкторе дочернего класса B мы хотим поймать это исключение.
// Пример 4.10 - перехват исключения из конструктора предка
class A {
public:
A(int x) {
if (x <= 0) throw std::invalid_argument("x <= 0");
}
};
class B : public A {
public:
B(int x) try : A(x) {
// тело конструктора
} catch (std::invalid_argument) {
std::cerr << "Перехвачено исключение из конструктора родителя";
}
};
Интересной особенностью такой конструкции try-catch является то, что исключение, пойманное в блоке catch, не будет считаться обработанным даже несмотря на отсутствие оператора throw для повторного возбуждения исключения. Действительно, поскольку объект класса B не смог корректно создаться, то нормальное продолжение программы невозможно, и будет выполнен поиск следующего обработчика данного исключения.
