Программирование >>  Решение нетривиальных задач 

1 ... 73 74 75 [ 76 ] 77


c::~c( )

delete pi;

Запомните, что pi содержит мусор до своей инициализации оператором new. Если возбуждается исключение до вызова new или сам оператор new возбудит исключение, то тогда pi никогда не инициализируется. (Вероятно, оно не будет содержать null, а будет просто не инициализированно). Когда вызывается деструктор, то оператору delete передается это неопределенное значение. Решим проблему, инициализировав этот указатель безопасным значением до того, как что-либо испортится:

c::c( ) : pi(NULL) инициализируется на случай, если оператор

new даст сбой

if( some error() )

throw error( this); Это возбуждение теперь безопасно.

...

pi = new int[128]; Сбой оператора new теперь безопасен.

...

if( some other error() ) {

delete [] pi; Не забудьте высвободить динамическую

память.

throw error( this); Это возбуждение безопасно.

деструктора незавершенный конструктор должен привести объект в исходное состояние перед тем, как сможет возбудить ошибку. С учетом предшествующего определения класса c следующий код будет работать при условии, что отсутствует ошибка до оператора new int [12 8] и new выполнен успешно:

c::c( )

if( some error() )

throw error(this); ДЕФЕКТ: pi не инициализирован.

...

pi = new int[128]; ДЕФЕКТ: pi не инициализирован,

если оператор new возбуждает исключение.

...

if( some other error() ) {

delete [] pi; Не забудьте сделать это.

throw error(this); Это возбуждение безопасно



c::~c( )

if( pi )

delete pi;

Следует помнить, что нужно освобождать успешно выделенную память, если исключение возбуждено после операции выделения, так, как было сделано ранее.

У вас есть возможность почистить предложенный выше код при его использовании с учетом моего совета из предыдущего правила о возбуждении исключения объекта error и скрытия всех сложностей в этом объекте. Однако определение этого класса получается значительно более сложным. Реализация в листинге 16 опирается на тот факт, что деструктор явно объявленного объекта должен вызываться при выходе из try-блока, перед выполнением catch-блока. Деструктор для объекта, полученного при помощи new, не будет вызван до тех пор, пока память не будет передана оператору delete, что происходит в сообщении destroy(), посланном из оператора catch. Следовательно, переменная has been destroyed будет содержать истину, если объект получен не при помощи new, и исключение возбуждено из конструктора, и ложь - если объект получен посредством new, потому что деструктор еще не вызван.

Конечно, вы можете вполне резонно заметить, что у меня нет причин проверять содержимое объекта, который по теории должен быть уничтожен. Здесь уже другая проблема. Некоторые компиляторы (в том числе компилятор Microsoft Visual С++ 2.2) вызывают деструктор после выполнения оператора catch, даже если объекты, определенные в try-блоке, недоступны из catch-блока. Следовательно, код из листинга 16 не будет работать после этих компиляторов. Вероятно, лучшие решение состояло бы в написании варианта operator new() , который мог бы надежно указывать, получена память из кучи или из стека.



class с

public:

class error

c *p; NULL при успешном выполнении

конструктора

public:

error( c *p this );

void destroy( void );

private:

unsigned has been destroyed : 1;

int *pi;

private: friend class error;

int been destroyed( void );

public:

c() ;

~c();

24 25 26

error::error( c *p this ) : p( p this ) {}

void c::error::destroy( void )

if( p && !p->been destroyed() )

delete p;

32 33 34

c() : has been destroyed( 0 )

...

throw error( this);

...

:~c()

...

has beeb destroyed = 1;

int c::been destroyed( void )

return has been destroyed;

Листинг 16. except.cpp - возбуждение исключения из конструктора



1 ... 73 74 75 [ 76 ] 77

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика