К сожалению, не существует стандартного, переносимого и ясного способа реализовать проверку выхода за пределы допустимого диапазона с помощью операции [] в классе vector []. Однако эту проверку в классах vector и string можно реализовать намного точнее и полнее. Хотя обычно это связано с заменой реализации стандартной библиотеки, уточнением опций инсталляции или с вмешательством в код стандартной библиотеки. Ни одна из этих возможностей неприемлема для новичков, приступающих к программированию, поэтому мы использовали класс string из главы 2.
19.5. Ресурсы и исключения
Таким образом, объект класса vector может генерировать исключения, и мы рекомендуем, чтобы, если функция не может выполнить требуемое действие, она генерировала исключение и передавала сообщение в вызывающий модуль (см. главу 5). Теперь настало время подумать, как написать код, обрабатывающий исключения, сгенерированные операторами класса vector и другими функциями. Наивный ответ — “для перехвата исключения используйте блок try, пишите сообщение об ошибке, а затем прекращайте выполнение программы” — слишком прост для большинства нетривиальных систем.
• Память (memory).
• Блокировки (locks).
• Дескрипторы файлов (file handles).
• Дескрипторы потоков (thread handles).
• Сокеты (sockets).
• Окна (windows).
new, и возвращаем с помощью оператора delete. Рассмотрим пример.
void suspicious(int s, int x)
{
int* p = new int[s]; // занимаем память
// ...
delete[] p; // освобождаем память
}
Как мы видели в разделе 17.4.6, следует помнить о необходимости освободить память, что не всегда просто выполнить. Исключения еще больше усугубляют ситуацию, и в результате из-за невежества или небрежности может возникнуть утечка ресурсов. В качестве примера рассмотрим функцию suspicious(), которая использует оператор new явным образом и присваивает результирующий указатель на локальную переменную, создавая очень опасную ситуацию.
19.5.1. Потенциальные проблемы управления ресурсами
int* p = new int[s]; // занимаем память
Она заключается в трудности проверки того, что данному оператору new соответствует оператор delete. В функции suspicious() есть инструкция delete[] p, которая могла бы освободить память, но представим себе несколько причин, по которым это может и не произойти. Какие инструкции можно было бы вставить в часть, отмеченную многоточием, ..., чтобы вызвать утечку памяти? Примеры, которые мы подобрали для иллюстрации возникающих проблем, должны натолкнуть вас на размышления и вызвать подозрения относительно такого кода. Кроме того, благодаря этим примерам вы оцените простоту и мощь альтернативного решения.
Возможно, указатель p больше не ссылается на объект, который мы хотим уничтожить с помощью оператора delete.
void suspicious(int s, int x)
{
int* p = new int[s]; // занимаем память
// ...
if (x) p = q; // устанавливаем указатель p на другой объект
// ...
delete[] p; // освобождаем память
}
Мы включили в программу инструкцию if (x), чтобы гарантировать, что вы не будете знать заранее, изменилось ли значение указателя p или нет. Возможно, программа никогда не выполнит оператор delete.
void suspicious(int s, int x)
{
int* p = new int[s]; // занимаем память
// ...
if (x) return;
// ...
delete[] p; // освобождаем память
}
Возможно, программа никогда не выполнит оператор delete, потому что сгенерирует исключение.
void suspicious(int s, int x)
{
int* p = new int[s]; // занимаем память
vector
// ...
if (x) p[x] = v.at(x);
// ...
delete[] p; // освобождаем память
}