Читаем Программирование полностью

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

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

{

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

  vector v;

  // ...

  try {

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

    // ...

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

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

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

  }

  // ...

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

}

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

void suspicious(vector& v, int s)

{

  int* p = new int[s];

  vectorv1;

  // ...

  int* q = new int[s];

  vector v2;

  // ...

  delete[] p;

  delete[] q;

}

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

ПОПРОБУЙТЕ

Добавьте блоки try в последний пример и убедитесь, что все ресурсы будут правильно освобождаться при любых исключениях.

<p id="AutBody_Root365"><strong>19.5.2. Получение ресурсов — это инициализация</strong></p>

 К счастью, нам не обязательно копировать инструкции try...catch, чтобы предотвратить утечку ресурсов. Рассмотрим следующий пример:

void f(vector& v, int s)

{

  vector p(s);

  vector q(s);

  // ...

} 

  Это уже лучше. Что еще более важно, это очевидно лучше. Ресурс (в данном случае свободная память) занимается конструктором и освобождается соответствующим деструктором. Теперь мы действительно решили нашу конкретную задачу, связанную с исключениями. Это решение носит универсальный характер; его можно применить ко всем видам ресурсов: конструктор получает ресурсы для объекта, который ими управляет, а соответствующий деструктор их возвращает. Такой подход лучше всего зарекомендовал себя при работе с блокировками баз данных (database locks), сокетами (sockets) и буферами ввода-вывода (I/O buffers) (эту работу делают объекты класса iostream). Соответствующий принцип обычно формулируется довольно неуклюже: “Получение ресурса есть инициализация” (“Resource Acquisition Is Initialization” — RAII).

Рассмотрим предыдущий пример. Как только мы выйдем из функции f(), будут вызваны деструкторы векторов p и q: поскольку переменные p и q не являются указателями, мы не можем присвоить им новые значения, инструкция return не может предотвратить вызов деструкторов и никакие исключения не генерируются.

Это универсальное правило: когда поток управления покидает область видимости, вызываются деструкторы для каждого полностью созданного объекта и активизированного подобъекта. Объект считается полностью созданным, если его конструктор закончил свою работу. Исследование всех следствий, вытекающих из этих двух утверждений, может вызвать головную боль. Будем считать просто, что конструкторы и деструкторы вызываются, когда надо и где надо.

  В частности, если хотите выделить в области видимости свободную память переменного размера, мы рекомендуем использовать класс vector, а не “голые” операторы new и delete

<p id="AutBody_Root366"><strong>19.5.3. Гарантии</strong></p>

Что делать, если вектор невозможно ограничить только одной областью (или подобластью) видимости? Рассмотрим пример.

vector* make_vec() // создает заполненный вектор

{

  vector* p = new vector; // выделяем свободную память

  // ...заполняем вектор данными;

  // возможна генерация исключения...

  return p;

}

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже