Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Итог_Пособие C++.doc
Скачиваний:
8
Добавлен:
01.07.2025
Размер:
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

}