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

auto_ptr type_pointed_to identifier;

Здесь type_pointed_to представляет собой тип нужного объекта. Рассмотрим последовательно каждое из этих определений. Как правило, мы хотим непосредственно инициализировать объект auto_ptr адресом объекта, созданного с помощью оператора new. Это можно сделать следующим образом:

auto_ptr int pi ( new int( 1024 ) );

В результате значением pi является адрес созданного объекта, инициализированного числом 1024. С объектом, на который указывает auto_ptr, можно работать обычным способом:

if ( *pi != 1024 )

// ошибка, что-то не так

else *pi *= 2;

Объект, на который указывает pi, будет автоматически уничтожен по окончании времени жизни pi. Если указатель pi является локальным, то объект, который он адресует, будет уничтожен при выходе из блока, где он определен. Если же pi глобальный, то объект, на который он ссылается, уничтожается при выходе из программы.

Что будет, если мы инициализируем auto_ptr адресом объекта класса, скажем, стандартного класса string? Например:

auto_ptr string

pstr_auto( new string( "Brontosaurus" ) );

Предположим, что мы хотим выполнить какую-то операцию со строками. С обычной строкой мы бы поступили таким образом:

string *pstr_type = new string( "Brontosaurus" );

if ( pstr_type-empty() )

// ошибка, что-то не так

А как обратиться к операции empty(), используя объект auto_ptr? Точно так же:

auto_ptr string pstr_auto( new string( "Brontosaurus" ) );

if ( pstr_type-empty() )

// ошибка, что-то не так

Создатели шаблона класса auto_ptr не в последнюю очередь стремились сохранить привычный синтаксис, употребляемый с обычными указателями, а также обеспечить дополнительные возможности автоматического удаления объекта, на который ссылается auto_ptr. При этом время выполнения не увеличивается. Применение встроенных функций (которые подставляются по месту вызова) позволило сделать использование объекта auto_ptr немногим более дорогим, чем непосредственное употребление указателя.

Что произойдет, если мы проинициализируем pstr_auto2 значением pstr_auto, который является объектом auto_ptr, указывающим на строку?

// кто несет ответственность за уничтожение строки?

auto_ptr string pstr_auto2( pstr_auto );

Представим, что мы непосредственно инициализировали один указатель на строку другим:

string *pstr_type2( pstr_type );

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

В противоположность этому шаблон класса auto_ptr поддерживает понятие владения. Когда мы определили pstr_auto, он стал владельцем строки, адресом которой был инициализирован, и принял на себя ответственность за ее уничтожение.

Вопрос в том, кто станет владельцем строки, когда мы инициализируем pstr_auto2 адресом, указывающим на тот же объект, что и pstr_auto? Нежелательно, чтобы оба объекта владели одной и той же строкой: это вернет нас к проблемам повторного удаления, от которых мы стремились уйти с помощью шаблона класса auto_ptr.

Когда один объект auto_ptr инициализируется другим или получает его значение в результате присваивания, одновременно он получает и право владения адресуемым объектом. Объект auto_ptr, стоящий справа от оператора присваивания, передает право владения и ответственность auto_ptr, стоящему слева. В нашем примере ответственность за уничтожение строки несет pstr_auto2, а не pstr_auto. pstr_auto больше не может употребляться для ссылки на эту строку.

Аналогично ведет себя и операция присваивания. Пусть у нас есть два объекта auto_ptr:

auto_ptr int p1( new int( 1024 ) );

auto_ptr int p2( new int( 2048 ) );

Мы можем скопировать один объекта auto_ptr в другой с помощью этой операции:

p1 = p2;

Перед присваиванием объект, на который ссылался p1, удаляется.

После присваивания p1 владеет объектом типа int со значением 2048. p2 больше не может использоваться как ссылка на этот объект.

Третья форма определения объекта auto_ptr создает его, но не инициализирует значением указателя на область памяти из хипа. Например:

// пока не ссылается ни на какой объект

auto_ptr int p_auto_int;

Поскольку p_auto_int не инициализирован адресом какого-либо объекта, значение хранящегося внутри него указателя равно 0. Разыменование таких указателей приводит к непредсказуемому поведению программы:

// ошибка: разыменование нулевого указателя

if ( *p_auto_int != 1024 )

*p_auto_int = 1024;

Обычный указатель можно проверить на равенство 0: