К сожалению, не существует стандартного, переносимого и ясного способа реализовать проверку выхода за пределы допустимого диапазона с помощью операции []
в классе 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; // освобождаем память
}