Это довольно распространенный пример: мы вызываем функцию, чтобы создать сложную структуру данных, и возвращаем эту структуру как результат. Однако, если при заполнении вектора возникнет исключение, функция make_vec()
потеряет этот объект класса vector
. Кроме того, если функция успешно завершит работу, то кто-то будет должен удалить объект, возвращенный функцией make_vec()
(см. раздел 17.4.6).
Для того чтобы сгенерировать исключение, мы можем добавить блок try
.
vector
{
vector
try {
// ...заполняем вектор данными;
// возможна генерация исключения...
return p;
}
catch (...) {
delete p; // локальная очистка
throw; // повторно генерируем исключение,
// чтобы вызывающая
// функция отреагировала на то, что функция
// make_vec() не сделала то, что требовалось
}
}
make_vec()
иллюстрирует очень распространенный стиль обработки ошибок: программа пытается выполнить свое задание, а если не может, то освобождает все локальные ресурсы (в данном случае свободную память, занятую объектом класса vector
) и сообщает об этом, генерируя исключение. В данном случае исключение генерируется другой функцией ((vector::at()
); функция make_vec()
просто повторяет генерирование с помощью оператора throw
;.
Это простой и эффективный способ обработки ошибок, который можно применять систематически.
• try ... catch
состоит в том, чтобы гарантировать, что функция make_vec()
либо завершит работу успешно, либо сгенерирует исключение без утечки ресурсов. Это часто называют
•
• throw
, new
и не применяйте оператор dynamic_cast к ссылочным типам (раздел A.5.7).
Для анализа правильности программы наиболее полезными являются базовая и жесткая гарантии. Принцип RAII играет существенную роль для реализации простого и эффективного кода, написанного в соответствии с этими идеями. Более подробную информацию можно найти в приложении Д книги
19.5.4. Класс auto_ptr
Итак, функции, такие как make_vec()
, подчиняются основным правилам корректного управления ресурсами с использованием исключений. Это обеспечивает выполнение базовой гарантии, которую должны давать все правильные функции при восстановлении работы программы после генерирования исключений. Если не произойдет чего-либо катастрофического с нелокальными данными в той части программы, которая ответственна за заполнение вектора данными, то можно даже утверждать, что такие функции дают жесткую гарантию. Однако этот блок try ... catch
по-прежнему выглядит ужасно. Решение очевидно: нужно как-то применить принцип RAII; иначе говоря, необходимо предусмотреть объект, который будет владеть объектом класса vector
и сможет его удалить, если возникнет исключение. В заголовке
стандартной библиотеки содержится класс auto_ptr
, предназначенный именно для этого.
vector
{