auto_ptr
В стандартном заголовочном файле <memory> auto_ptr объявлен следующим образом...
Ввиду того, что после выхода первых (английских) тиражей стандарт претерпел некоторые изменения в части auto_ptr, концовку данного раздела следует заменить следующим текстом (он взят из списка авторских исправлений к 4 тиражу).
Для достижения данной семантики владения (также называемой семантикой разрушающего копирования (destructive copy semantics)), семантика копирования шаблона auto_ptr радикально отличается от семантики копирования обычных указателей: когда один auto_ptr копируется или присваивается другому, исходный auto_ptr очищается (эквивалентно присваиванию 0 указателю). Т.к. копирование auto_ptr приводит к его изменению, то const auto_ptr не может быть скопирован.
Шаблон auto_ptr определен в <memory> следующим образом: template<class X> class std::auto_ptr { // вспомогательный класс template <class Y> struct auto_ptr_ref { /* ... */ };
X* ptr; public: typedef X element_type;
explicit auto_ptr(X* p =0) throw() { ptr=p; } ~auto_ptr() throw() { delete ptr; }
// обратите внимание: конструкторы копирования и операторы // присваивания имеют неконстантные аргументы
// скопировать, потом a.ptr=0 auto_ptr(auto_ptr& a) throw();
// скопировать, потом a.ptr=0 template<class Y> auto_ptr(auto_ptr<Y>& a) throw();
// скопировать, потом a.ptr=0 auto_ptr& operator=(auto_ptr& a) throw();
// скопировать, потом a.ptr=0 template<class Y> auto_ptr& operator=(auto_ptr<Y>& a) throw();
X& operator*() const throw() { return *ptr; } X* operator->() const throw() { return ptr; }
// вернуть указатель X* get() const throw() { return ptr; }
// передать владение X* release() throw() { X* t = ptr; ptr=0; return t; }
void reset(X* p =0) throw() { if (p!=ptr) { delete ptr; ptr=p; } }
// скопировать из auto_ptr_ref auto_ptr(auto_ptr_ref<X>) throw();
// скопировать в auto_ptr_ref template<class Y> operator auto_ptr_ref<Y>() throw();
// разрушающее копирование из auto_ptr template<class Y> operator auto_ptr<Y>() throw(); };
Назначение auto_ptr_ref -- обеспечить семантику разрушающего копирования, ввиду чего копирование константного auto_ptr становится невозможным. Конструктор-шаблон и оператор присваивания-шаблон обеспечивают возможность неявного пребразования auto_ptr<D> в auto_ptr<B> если D* может быть преобразован в B*, например: void g(Circle* pc) { auto_ptr<Circle> p2 = pc; // сейчас p2 отвечает за удаление
auto_ptr<Circle> p3 = p2; // сейчас p3 отвечает за удаление, // а p2 уже нет
p2->m = 7; // ошибка программиста: p2.get()==0
Shape* ps = p3.get(); // извлечение указателя
auto_ptr<Shape> aps = p3; // передача прав собственности и // преобразование типа
auto_ptr<Circle> p4 = pc; // ошибка: теперь p4 также отвечает за удаление }
Эффект от использования нескольких auto_ptr для одного и того же объекта неопределен; в большинстве случаев объект будет уничтожен дважды, что приведет к разрушительным результатам.
Следует отметить, что семантика разрушающего копирования не удовлетворяет требованиям к элементам стандартных контейнеров или стандартных алгоритмов, таких как sort(). Например: // опасно: использование auto_ptr в контейнере void h(vector<auto_ptr<Shape> >& v) { sort(v.begin(),v.end()); // не делайте так: элементы не будут отсортированы }
Понятно, что auto_ptr не является обычным "умным" указателем, однако он прекрасно справляется с предоставленной ему ролью -- обеспечивать безопасную относительно исключений работу с автоматическими указателями, и делать это без существенных накладных расходов.