Выбрать главу

// Обработка исключений, генерируемых оператором new.

#include <iostream>

#include <new>

using namespace std;

int main()

{

 int *p, i;

 try {

  p = new int[32]; // запрос на выделение памяти для 32-элементного int-массива

 }

 catch (bad_alloc ха) {

  cout << "Память не выделена.\n";

  return 1;

 }

 for(i=0; i<32; i++) p[i] = i;

 for(i=0; i<32; i++ ) cout << p[i] << " ";

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

 return 0;

}

При неудачном выполнении оператора new исключение в этой программе будет перехвачено catch-инструкцией. Этот же подход можно использовать для отслеживания любых ошибок, связанных с использованием оператора new: достаточно заключить каждую new-инструкцию в try-блок.

Альтернативная форма оператора new — nothrow

Стандарт C++ при неудачной попытке выделения памяти вместо генерирования исключения также позволяет оператору new возвращать значение null. Эта форма использования оператора new особенно полезна при компиляции старых программ с применением современного С++-компилятора. Это средство также очень полезно при замене вызовов функции malloc() оператором new. (Это обычная практика при переводе С-кода на язык C++.) Итак, этот формат оператора new выглядит следующим образом.

p_var = new(nothrow) тип;

Здесь элемент p_var— это указатель на переменную типа тип. Этот nothrow-формат оператора new работает подобно оригинальной версии оператора new, которая использовалась несколько лет назад. Поскольку оператор new (nothrow) возвращает при неудаче значение null, его можно "внедрить" в старый код программы, не прибегая к обработке исключений. Однако в новых программах на C++ все же лучше иметь дело с исключениями.

В следующем примере показано, как используется альтернативный вариант new (nothrow). Нетрудно догадаться, что перед вами вариация на тему предыдущей программы.

// Использование nothrow-версии оператора new.

#include <iostream>

#include <new>

using namespace std;

int main()

{

 int *p, i;

 p = new(nothrow) int[32]; // использование nothrow-версии

 if(!p) {

  cout << "Память не выделена.\n";

  return 1;

 }

 for(i=0; i<32; i++) p[i] = i;

 for(i=0; i<32; i++ ) cout << p[i] << " ";

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

 return 0;

}

Здесь при использовании nothrow-версии после каждого запроса на выделение памяти необходимо проверять значение указателя, возвращаемого оператором new.

Перегрузка операторов new и delete

Поскольку new и delete — операторы, их также можно перегружать. Несмотря на то что перегрузку операторов мы рассматривали в главе 13, тема перегрузки операторов new и delete была отложена до знакомства с темой исключений, поскольку правильно перегруженная версия оператора new (та, которая соответствует стандарту C++) должна в случае неудачи генерировать исключение типа bad_alloc. По ряду причин вам имеет смысл создать собственную версию оператора new. Например, создайте процедуры выделения памяти, которые, если область кучи окажется исчерпанной, автоматически начинают использовать дисковый файл в качестве виртуальной памяти. В любом случае реализация перегрузки этих операторов не сложнее перегрузки любых других.

Ниже приводится скелет функций, которые перегружают операторы new и delete.

// Выделение памяти для объекта.