The <memory> header declares function and class templates for allocating and using memory, such as the auto_ptr<> smart pointer, algorithms for working with uninitialized memory, and a standard allocator for use with the standard containers.
The auto_ptr class template provides a simple ownership model for working with pointers. It can be extremely useful for writing exception-safe code. On the other hand, copying an auto_ptr<> object does not produce an exact copy (ownership of the pointer is transferred), so auto_ptr<> objects cannot be stored in standard containers.
Several functions work with uninitialized memory, which can be helpful when implementing a container. For example, an implementation of vector must allocate an uninitialized array of objects and initialize elements of the array as they are needed. The uninitialized_ . . . functions can come in handy for that purpose.
The allocator class template manages memory allocation and deallocation and the construction and destruction of objects in the memory it manages. It is the default allocator for all the standard containers.
allocator class template | Encapsulates memory allocation and deallocation |
template <class T> class allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; template <class U> struct rebind { typedef allocator<U> other; }; allocator( ) throw( ); allocator(const allocator&) throw( ); template <class U> allocator(const allocator<U>&) throw( ); ~allocator( ) throw( ); pointer address(reference x) const; const_pointer address(const_reference x) const; pointer allocate(size_type, allocator<void>::const_pointer hint = 0); void deallocate(pointer p, size_type n); size_type max_size( ) const throw( ); void construct(pointer p, const T& val); void destroy(pointer p); }; |
The allocator class template encapsulates basic allocation and deallocation functions. The standard containers rely on allocators for memory management and use allocator as the default allocator.
Most programmers do not need to use allocator, which offers few advantages over plain new and delete. However, if you want to write your own container, or provide a custom allocator for the standard containers, you should take the time to understand allocator.
Perhaps the easiest way to understand allocator is to take a look at a trivial implementation in Example 13-30. Note that a library might have a more complicated implementation to handle multithreading, improve performance, etc. Some libraries offer allocators for special purposes, such as allocating memory that can be shared among multiple processes. This particular implementation is just a sample.
template<typename T> class myallocator { public: typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; template <class U> struct rebind { typedef myallocator<U> other; }; myallocator( ) throw( ) {} myallocator(const myallocator&) throw( ) {} template <class U> myallocator(const myallocator<U>&) throw( ) {} ~myallocator( ) throw( ) {} pointer address(reference x) const {return &x;} const_pointer address(const_reference x) const {return &x;} pointer allocate(size_type n, void* hint = 0) { return static_cast<T*>(::operator new (n * sizeof(T)) ); } void deallocate(pointer p, size_type n) { ::operator delete(static_cast<void*>(p)); } size_type max_size( ) const throw( ) { return std::numeric_limits<size_type>::max( ) / sizeof(T); } void construct(pointer p, const T& val) { new(static_cast<void*>(p)) T(val); } void destroy(pointer p) { p->~T( ); } }; template<typename T> bool operator==(const myallocator<T>&, const myallocator<T>&) { return true; } template<typename T> bool operator!=(const myallocator<T>&, const myallocator<T>&) { return false; } template<> class myallocator<void> { public: typedef void* pointer; typedef const void* const_pointer; typedef void value_type; template <class U> struct rebind { typedef myallocator<U> other; }; };
The following are the members of allocator:
Constructs a new allocator object, possibly copying an existing allocator. Remember that all instances must be equivalent, so an allocator typically does not have any data members to initialize.
Returns the address of x, that is, &x.
Calls the global new operator to allocate enough memory to hold n objects of type T. The hint argument must be 0 or a pointer obtained from a prior call to allocate that has not been passed to deallocate. The return value is a pointer to the newly allocated memory. If the memory cannot be allocated, bad_alloc is thrown.
An implementation might use hint to improve performance.
A type for a pointer to const. In a custom allocator, the type should be equivalent to const T*.
A type for a const lvalue. In a custom allocator, the type should be equivalent to const T&.
Calls the global new operator to construct an instance of T with value val using the memory that p points to. That is, it calls new(static_cast<void*>(p)) T(val).
Calls the global delete operator to free the memory that p points to. The n argument is the number of items of type Tthe same value passed to allocate.
Calls the destructor for the object at address p. That is, it calls reinterpret_cast<T*>(p)->~T( ).
A type that represents the difference of any two pointers that the allocator returns from allocate( ).
Returns the maximum size that can be passed to allocate.
A pointer type. In a custom allocator, the type should be equivalent to T*.
Binds the allocator object to a different value type. The rebind class has a single typedef, other, which is an instance of the same allocator template, but with U as the template parameter. The rebind template is necessary for standard containers that allocate helper objects, such as link nodes, rather than allocating values directly. If you are not implementing a standard container, you probably don't need to understand rebind.
An lvalue type. In a custom allocator, the type should be equivalent to T&.
A type that can represent the size of the largest allocation request.
The type of allocated values, which is typically T.
allocator<void> class, <new>, new operator, delete operator
allocator<void> class | Specializes allocator for void pointers |
template <> class allocator<void> {
public:
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class U> struct rebind {
typedef allocator<U> other;
};
};
|
The allocator<void> specialization is necessary to represent pointers to void without permitting the allocation of objects of type void.
allocator class template
auto_ptr class template | Smart pointer to manage ownership of pointers |
template <class T> struct auto_ptr_ref {}; template<class T> class auto_ptr { public: typedef T element_type; explicit auto_ptr(T* p = 0) throw( ); auto_ptr(auto_ptr&) throw( ); template<class U> auto_ptr(auto_ptr<U>&) throw( ); auto_ptr(auto_ptr_ref<T>) throw( ); ~auto_ptr( ) throw( ); auto_ptr& operator=(auto_ptr&) throw( ); template<class U> auto_ptr& operator=(auto_ptr<U>&) throw( ); auto_ptr& operator=(auto_ptr_ref<T> r) throw( ); T& operator*( ) const throw( ); T* operator->( ) const throw( ); T* get( ) const throw( ); T* release( ) throw( ); void reset(T* p = 0) throw( ); template<class U> operator auto_ptr_ref<U>( ) throw( ); template<class U> operator auto_ptr<U>( ) throw( ); }; |
The auto_ptr class template implements a smart pointer to manage ownership of pointers. Proper use of auto_ptr ensures that a pointer has exactly one owner (which prevents accidental double deletes), and the owner automatically frees the memory when the owner goes out of scope (which prevents memory leaks). Assignment of auto_ptr values transfers ownership from the source to the target of the assignment.
The auto_ptr_ref type holds a reference to an auto_ptr. Implicit conversions between auto_ptr and auto_ptr_ref facilitate the return of auto_ptr objects from functions. Usually, you can ignore the auto_ptr_ref type and let the implicit conversions handle the details for you. All you need to do is use auto_ptr as a return type. The details of auto_ptr_ref are implementation-defined.
Some of the typical uses for auto_ptr are:
Data members of pointer type that point to dynamically allocated objects are prime candidates for auto_ptr, which ensures that the memory is properly freed when the owning object is destroyed. Be sure to implement a copy constructor and assignment operator for any class that uses auto_ptr<> for its data members.
If a function must dynamically allocate a temporary object, store the pointer in an auto_ptr variable. When the function returns (normally or as the result of an exception), the object is destroyed automatically. This can drastically reduce the need for try-catch statements.
In a complex program, objects are frequently allocated in one part of the program and freed in another. It can be difficult to keep track of when it is safe or proper to free an object. Using auto_ptr, you can safely ensure that each object has exactly one owner, and ownership is properly passed via assignment and function calls. When the object is no longer needed, it is freed automatically.
Because you cannot simply copy or assign an auto_ptr, you cannot use auto_ptr objects in a standard container. Another limitation is that auto_ptr cannot hold a pointer to an array. Allocating and freeing a single object (e.g., new int) is different from allocating and freeing an array of objects, (e.g., new int[42]), and auto_ptr is designed to work only with single objects.
|
The Boost project has additional smart-pointer class templates that permit copying, arrays, and shared ownership. See Appendix B for more information about Boost.
Example 13-31 shows some uses of auto_ptr.
class brush { . . . }; class pen { . . . }; // A function can return an auto_ptr<> object. std::auto_ptr<brush> default_brush( ) { return std::auto_ptr<brush>(new brush); } class DisplayContext { // Display or graphics context for drawing on a window. public: DisplayContext( ) : brush_(default_brush( )), pen_(new pen) {...} . . . private: // Make sure caller never tries to copy or assign // DisplayContext, but uses only objects that are // managed by auto_ptr<>. DisplayContext(const DisplayContext& dc); DisplayContext& operator=(const DisplayContext& dc); // Automatically manage lifetime of the pen and brush. // When the DisplayContext is freed, so are the pen // and brush instances. std::auto_ptr<brush> brush_; std::auto_ptr<pen> pen_; }; void repaint( ) { // Allocate a new display context. Use auto_ptr to ensure // that it will be freed automatically. std::auto_ptr<DisplayContext> dc(new DisplayContext( )); // Draw stuff on the display context. dc->draw( . . . ); // No need to call release; the display context is // automatically released when repaint( ) returns. } int main( ) { std::auto_ptr<DisplayContext> dc1(new DisplayContext); std::auto_ptr<DisplayContext> dc2(dc1); dc1 = dc2; repaint( ); }
The following are the members of auto_ptr:
Initializes the auto_ptr object to own the pointer p.
Initializes the auto_ptr object with the pointer returned from x.release( ). In the second version, the type U* must be implicitly convertible to T*. Note that x is not const. It is not possible to copy a const auto_ptr because to do so would break the ownership rules.
Initializes the auto_ptr object with the pointer obtained from calling release on r's auto_ptr.
Deletes the owned pointer (e.g., delete get( )).
Returns the owned pointer.
Returns get( ) and resets the owned pointer to 0.
Deletes the owned pointer (if it is not equal to p) and saves p as the new owned pointer.
Returns a temporary auto_ptr_ref object that owns the pointer. The pointer must be convertible to U*. Ownership is released and transferred to the new auto_ptr_ref object.
Returns a new auto_ptr object. The owned pointer is converted to type U*, and ownership is transferred to the new auto_ptr object.
Transfers ownership of the pointer that is owned by x or by the auto_ptr object held by r to *this. That is, it calls reset(x.release( )).
Returns *get( ). If the owned pointer is a null pointer, the behavior is undefined.
Returns get( ).
new operator
get_temporary_buffer function template | Allocates temporary memory buffer |
template <class T>
pair<T*, ptrdiff_t> get_temporary_buffer(ptrdiff_t n);
|
The get_temporary_buffer function template allocates memory for temporary use. The request is for up to n adjacent objects of type T. The return value is a pair of the pointer to the newly allocated memory and the actual size of the memory allocated (in units of sizeof(T)). If the memory cannot be allocated, the return value is a pair of 0s. The allocated memory must be freed by calling return_temporary_buffer. The temporary buffer is uninitialized.
This function has limited usefulness. You must test the return value to see how much memory was allocated and ensure that the memory is properly freed if an exception is thrown. It is usually simpler to call new and save the pointer in an auto_ptr<>.
auto_ptr class template, return_temporary_buffer function template, pair in <utility>
operator== function template | Compares allocators for equality |
template <class T1, class T2>
bool operator==(const allocator<T1>&, const allocator<T2>&)
throw( );
|
The operator== function template always returns true. In other words, any object of type allocator is considered to be the same as every other allocator.
allocator class template
operator!= function template | Compares allocators for inequality |
template <class T1, class T2>
bool operator!=(const allocator<T1>&, const allocator<T2>&)
throw( );
|
The operator!= function template always returns false. In other words, any object of type allocator is considered to be the same as every other allocator.
allocator class template
raw_storage_iterator class template | Iterator for uninitialized memory |
template <class OutputIterator, class T> class raw_storage_iterator : public iterator<output_iterator_tag,void,void,void,void> { public: explicit raw_storage_iterator(OutputIterator x); raw_storage_iterator<OutputIterator,T>& operator*( ); raw_storage_iterator<OutputIterator,T>& operator=(const T& element); raw_storage_iterator<OutputIterator,T>& operator++( ); raw_storage_iterator<OutputIterator,T> operator++(int); }; |
The raw_storage_iterator class template implements an output iterator that writes to uninitialized memory. It adapts another output iterator that must have operator& return a pointer to T. The adapted iterator is typically used as a pointer to uninitialized memory.
Use the raw_storage_iterator as you would any other output iterator.
uninitialized_copy function template, uninitialized_fill function template, uninitialized_fill_n function template, <iterator>
return_temporary_buffer function template | Frees temporary memory buffer |
template <class T>
void return_temporary_buffer(T* p);
|
The return_temporary_buffer function reclaims the memory that was previously allocated by get_temporary_buffer.
get_temporary_buffer function template
uninitialized_copy function template | Copies into uninitialized memory |
template <class InputIter, class FwdIter>
FwdIter uninitialized_copy(InputIter first, InputIter last, FwdIter result);
|
The uninitialized_copy function template is like the copy algorithm, except the result iterator is assumed to point to uninitialized memory. The range [first, last) is copied to result using placement new.
raw_storage_iterator class template, uninitialized_fill function template, copy in <algorithm>, new operator
uninitialized_fill function template | Fills uninitialized memory |
template <class FwdIter, class T>
void uninitialized_fill(FwdIter first, FwdIter last, const T& x);
|
The uninitialized_fill function template is like the fill algorithm, except that it fills uninitialized memory. Every item in the range [first, last) is constructed as a copy of x using placement new.
raw_storage_iterator class template, uninitialized_copy function template, uninitialized_fill_n function template, fill in <algorithm>, new keyword
uninitialized_fill_n function template | Fills uninitialized memory |
template <class FwdIter, class Size, class T>
void uninitialized_fill_n(FwdIter first, Size n, const T& x);
|
The uninitialized_fill_n function template is like the fill_n algorithm, except it fills uninitialized memory. Starting with first, n copies of x are constructed using placement new.
raw_storage_iterator class template, uninitialized_fill function template, fill_n in <algorithm>, new keyword