- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •1. Основы языка 8
- •1.2.2 Простые типы данных 13
- •6. Паттерны проектирования 159
- •7. Антипаттерны 211
- •2. Работа с памятью 73
- •3. Основы объектно-ориентированного программирования 87
- •4.Обработка исключений 114
- •5. Шаблонные функции и классы. Библиотека стандартных шаблонов 130
- •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.6 Использование шаблонов для вычислений на этапе компиляции
- •5.5 Разработка шаблонных классов с настраиваемой функциональностью
- •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.9. Исключения в деструкторах
Большинство профессиональных программистов на языке C++ рекомендуют разрабатывать деструкторы классов по возможности так, чтобы они не могли возбуждать исключения (кроме исключений, обработанных внутри самого деструктора). Причина заключается в следующем. При обработке исключения происходит раскрутка стека, при этом вызываются деструкторы локальных объектов. Если в деструкторе локального объекта случится ещё одно исключение, не обработанное внутри деструктора, то будет вызвана функция terminate для аварийного завершения программы. При этом заранее сложно предсказать, в какой момент это произойдёт, то есть поведение программы будет близко к неопределённому.
Возникает вопрос: что делать, если в деструкторе нужно вызвать функцию, способную возбудить исключение? Понятно, что вызов такой функции можно поместить в блок try-catch, но не всегда ясно, что именно делать в блоке catch. Один из вариантов − корректно завершить работу программы, записав в файл журнала подробную информацию об ошибке.
5. Шаблонные функции и классы. Библиотека стандартных шаблонов
5.1 Шаблонные функции
Многие алгоритмы работают одинаково независимо от того, для обработки каких данных они применяются. Например, алгоритм сортировки методом пузырька будет выглядеть одинаково независимо от того, сортируем ли мы массив целых чисел, вещественных чисел, строк или объектов нашего собственного типа. Для того, чтобы не писать практически один и тот же код, отличающийся лишь типом обрабатываемых данных, в языке C++ имеется возможность создания шаблонов функций (часто также используется термин "шаблонная функция"). Шаблонная функции объявляется с помощью ключевого слова template следующим образом:
template <class|typename параметр_шаблона [, class|typename параметр_шаблона …]> дальнейшее объявление функции
В качестве параметров шаблона могут выступать:
∙ параметры-типы (их иногда называют фиктивными типами),
∙ параметры обычных типов (например, int) – см. пункт 5.6,
∙ параметры-шаблоны – см. пункт 5.4.
Для примера напишем шаблонную функцию sqr, которая возвращает квадрат своего аргумента:
// Пример 5.1 - простая шаблонная функция
template <class T>
T sqr (const T x) {
return x * x;
}
В данном примере параметр-тип (фиктивный тип) назван T (мы могли бы дать ему любое имя). При вызове шаблонной функции можно либо явно указать реальный тип параметра в угловых скобках, либо предоставить возможность компилятору определить его автоматически на основании значения аргумента функции. Например, следующие две строчки работают одинаково:
std::cout << sqr<int>(5) << " " << sqr<double>(3.14) << std::endl;
std::cout << sqr(5) << " " << sqr(3.14) << std::endl;
В первой строчке мы явно сказали, что реальными типами параметра являются int и double. Во второй строчке компилятор сам определит, что значение 5 имеет тип int, а значение 3.14 – тип double.
В этом примере компилятор сгенерирует две разных реализации (экземпляра) функции sqr – одна для типа int и другая – для типа double. Процесс создания нового экземпляра шаблонной функции или класса называется инстанцированием шаблона.
Приведём более сложный пример. Напишем шаблонную функцию count, возвращающую количество элементов в контейнере с, равных заданному значению value. В качестве контейнера может выступать, например, массив, вектор (std::vector), список (std::list) и др.
// Пример 5.2 - шаблонная функция count
template <class ContainerType, class ElementType>
int count(const ContainerType &c, const ElementType &value) {
int answer = 0;
for (auto &x : c)
if (x == value)
answer++;
return answer;
}
int main() {
std::vector<int> a = {1, 3, 7, 3, 2};
std::cout << count(a, 3) << std::endl; // будет выведен ответ 2
}
