Шаг 62.
Библиотека STL.
Вспомогательные средства. Класс auto_ptr. Предупреждение

    На этом шаге мы рассмотрим особенности передачи прав владения объектом.

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

// ПЛОХОЙ ПРИМЕР
template <class T>
void bad_print (std::auto_ptr <T> p)     // p становится владельцем
                                         // переданного аргумента
{
  // Указывает ли р на объект?
  if (p.get() == NULL) 
  { 
    std::cout << "NULL";
  } 
  else 
  {
    std::cout << *p;
  }
}    // Ошибка - обьект, на который ссылается р,
     // удаляется при выходе из функции.

    Каждый раз, когда этой реализации bad_print передается аргумент типа auto_ptr, принадлежащий ему объект (если он есть) уничтожается. Дело в том, что вместе со значением аргумента параметру р передается право владения объектом auto_ptr, а объект, принадлежащий р, удаляется при выходе из функции. Вряд ли программист учел такой поворот событий, поэтому, скорее всего, результатом будет фатальная ошибка времени выполнения:

std::auto_ptr <int> p(new int);
*p = 42;       // Изменение значения, на которое ссылается р 
bad_print(p);  // Удаление данных, на которые ссылается р 
*р = 18;       // ОШИБКА ВРЕМЕНИ ВЫПОЛНЕНИЯ

    Может, стоит передавать auto_ptr по ссылке? Однако передача auto_ptr по ссылке противоречит концепции владения. Нельзя быть полностью уверенным в том, что функция, получающая auto_ptr по ссылке, передаст (или не передаст) право владения. Передача auto_ptr по ссылке - очень плохое решение, никогда не используйте его в своих программах.

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

const std::auto_ptr <int> p(new int);
*p = 42;       // Изменение значения, на которое ссылается р
bad_print(p);  // ОШИБКА КОМПИЛЯЦИИ
*р = 18;       // ОК

    Такое решение повышает надежность auto_ptr. Многие интерфейсы используют константные ссылки для получения данных, которые проходят внутреннее копирование. Собственно говоря, все контейнерные классы стандартной библиотеки C++ ведут себя подобным образом:

template <class T>
void container::insert (const T& value)
{
  .    .    .    .
  x = value;  // Внутреннее копирование
  .    .    .    .
}

    Если бы такое присваивание было возможно для auto_ptr, то операция присваивания передала бы право владения контейнеру. Но из-за реальной архитектуры auto_ptr этот вызов приводит к ошибке на стадии компиляции:

контейнер <std:auto_ptr<int> > с; 
const std::auto_ptr<int> p(new int);
.    .    .    .
c.insert(p):  // ОШИБКА
.    .    .    .

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

    Константность не означает неизменности объекта, принадлежащего auto_ptr. Вы не сможете изменить право владения для константного экземпляра auto_ptr, однако ничто не мешает изменить значение, на которое он ссылается. Пример:

std::auto_ptr<int> f()
{
  const std::auto_ptr<int> p(new int); // Право владения не передается 
  std::auto_ptr<int> q(new int);     // Право владения передается
  *p = 42;  // OK: изменение значения, на которое ссылается р
  bad_print(p);  // ОШИБКА КОМПИЛЯЦИИ
  *р = *q; // OK; изменение значения, на которое ссылается р
  р = q;  // ОШИБКА КОМПИЛЯЦИИ
  return p;  // ОШИБКА КОМПИЛЯЦИИ
}

    Если const auto_ptr передается или возвращается в аргументе, любая попытка присвоить новый объект приводит к ошибке компиляции. В отношении константности const auto_ptr ведет себя как константный указатель (Т* const p), а не как указатель на константу (const T* р), хотя синтаксис вроде бы говорит об обратном.

    На следующем шаге мы рассмотрим использование этих объектов как переменных классов.




Предыдущий шаг Содержание Следующий шаг