Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

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<int> v;

// ...

if (x) p[x] = v.at(x);

// ...

delete[] p; // освобождаем память

}

Последняя возможность беспокоит нас больше всего. Когда люди впервые сталкиваются с такой проблемой, они считают, что она связана с исключениями, а не с управлением ресурсами. Не понимая истинных причин проблемы, они пытаются перехватывать исключения.

void suspicious(int s, int x) // плохой код

{

int* p = new int[s]; // занимаем память

vector<int> v;

// ...

try {

if (x) p[x] = v.at(x);

// ...

} catch (...) { // перехватываем все исключения

delete[] p; // освобождаем память

throw; // генерируем исключение повторно

}

// ...

delete[] p; // освобождаем память

}

Этот код решает проблему за счет дополнительных инструкций и дублирования кода, освобождающего ресурсы (в данном случае инструкции

delete[] p;
). Иначе говоря, это некрасивое решение; что еще хуже — его сложно обобщить. Представим, что мы задействовали несколько ресурсов.

void suspicious(vector<int>& v, int s)

{

int* p = new int[s];

vector<int>v1;

// ...

int* q = new int[s];

vector<double> v2;

// ...

delete[] p;

delete[] q;

}

Обратите внимание на то, что, если оператор

new
не сможет выделить свободную память, он сгенерирует стандартное исключение
bad_alloc
. Прием
try ... catc
h в этом примере также успешно работает, но нам потребуется несколько блоков
try
, и код станет повторяющимся и ужасным. Мы не любим повторяющиеся и запутанные программы, потому что повторяющийся код сложно сопровождать, а запутанный код не только сложно сопровождать, но и вообще трудно понять.

  • Читать дальше
  • 1
  • ...
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: