(C++) std::auto\_ptr
January 11, 2018 · View on GitHub
(C++) std::auto_ptr
The class template std::auto_ptr is deprecated [5]. The class template std::unique_ptr provides a better solution [5].
std::auto_ptr is a smart pointer that deletes the instance it points to when going out of scope. It is supplied in the STL header file memory.h.
std::auto_ptr helps to:
- Manage pointers and preventing memory leaks
- Clarifies the pointer management/ownership in classes
- Make code exception safe
Note: when you use a lot of forward declarations, you might want to prefer boost::scoped_ptr as it uses boost::checked_delete. Also, boost::scoped_ptr cannot be copied, so you will nearly ever be amazed by 'strange' behaviour.
Managing pointers and preventing memory leaks
Standard pointer:
struct MyClass {}; int main() { const MyClass const * p = new MyClass; p->doStuff(); delete p; }
Using an auto_ptr:
#include <memory> struct MyClass {}; int main() { const std::auto_ptr<MyClass> p(new MyClass); p->doStuff(); //Hey, the same way of accessing the pointed instance! //Done, std::auto_ptr deletes itself when going out of scope }
Use objects to manage resources (like dynamically allocated memory)[2] and store newed objects in smart pointers in standalone statements [3].
std::auto_ptr's does more then saving you a delete statement. It ensures that the instance pointed to is only pointed to once. This is done by a non-symettric copy: when you pass a pointer from std::auto_ptr to std::auto_ptr, the original possessor gets zero. This is demonstrated below:
#include <memory> #include <cassert> struct MyClass {}; int main() { const std::auto_ptr<MyClass> p1(new MyClass); const std::auto_ptr<MyClass> p2; assert(p1 != 0); assert(p2 == 0); p2 = p1; //Copies the MyClass* assert(p1 == 0); assert(p2 != 0); }
To get a copy to the pointed instance use the std::auto_ptr<T>::get member function:
#include <cassert> #include <memory> struct MyClass {}; int main() { const std::auto_ptr<MyClass> p1(new MyClass); const MyClass * const p2 = pClass.get(); assert(p1 !=0); assert(p2 !=0); }
Pointer management
Also, using a std::auto_ptr, it gets clear which class manages the deletion of the pointed instance.
class Something{}; struct MyClass { //Ownership remains at MyClass (*) const Something * const GetSomethingCopy() const { return mpSomething.get(); } //Ownership transferred to caller const std::auto_ptr<Something> GetSomething() //Cannot be a const-method! { return mpSomething; } private: std::auto_ptr<Something> mpSomething; }; // (*) Note that despite the constness, the pointer can be deleted by the caller, // without taking proper precautions (See Exercise #1: a foolproof function)
If the instance pointed to needs to be give to another class, then MyClass::GetSomething is left with an empty (that is: 0) pSomething.
Make code exception safe
When you create a new instance dynamically in a certain function using a plain pointer, in the end of this function you call delete. But when in the middle an exception is thrown, this delete is not called anymore! When using an auto_ptr, the instance DOES get deleted. This is because auto_ptr's delete their instances when they go out of scope.
Note, warnings, curiosities
Do not create an array dynamically using a std::auto_ptr
This will give you a memory leak, as a std::auto_ptr calls delete, instead of delete[]. As advised by [1], you should prefer a std::vector over an array. But if you really want to use a smart pointer, use a boost::scoped_array.
Do not put std::auto_ptr's in a std::vector [4]
A copy of a std::auto_ptr does not copy the memory address pointed to. Therefore, when using e.g. a sorting algorithm, some pointed instances might get deleted! Instead, use a boost::shared_ptr.
Resetting astd::auto_ptr
Resetting a std::auto_ptr first constructs a new instance of the class before deleting the old instance. This is demonstrated by the code below:
#include <iostream> #include <memory> struct Resetter { Resetter() { std::cout << "Constructor" << std::endl; } ~Resetter() { std::cout << "Destructor" << std::endl; } }; int main() { std::auto_ptr<Resetter> pReset(new Resetter); pReset.reset(new Resetter); }
This gives the following output:
Constructor Constructor Destructor Destructor
The reason for this behavior is I guess- exception safety: if the allocation of the new resources fail, then the old resources are not yet released.
References
- Herb Sutter and Andrei Alexandrescu. C++ coding standards: 101 rules, guidelines, and best practices. ISBN: 0-32-111358-6. 2004. Chapter 77: 'Use vector and string instead of arrays'
- Scott Meyers. Effective C++ (3rd edition). ISBN:0-321-33487-6. 2005. Item 13: 'Use objects to manage resources'
- Scott Meyers. Effective C++ (3rd edition). ISBN:0-321-33487-6. 2005. Item 17: 'Store newed objects in smart pointers in standalone statements'
- Scott Meyers. Effective STL. ISBN:0-201-74962-9. 2001. Item 8: 'Never create containers of auto_ptr's'
- Working Draft, Standard for Programming Language C++. 2014-08-22. N3936. Paragraph D.10. 'The class template auto_ptr is deprecated [Note: the class template unique_ptr provides a better solution -end note]'