270 likes | 375 Views
CIS 4930 Application Development Using C++ Dr. Kun Suk Kim CISE Department, University of Florida. Allocators. Objectives. Using allocators as an application programmer Using allocators as an library programmer The default allocator A user-defined allocator
E N D
CIS 4930Application Development Using C++Dr. Kun Suk KimCISE Department, University of Florida Allocators
Objectives • Using allocators as an application programmer • Using allocators as an library programmer • The default allocator • A user-defined allocator • Utilities for uninitialized memory • Ch 15
Allocators • Low-level mechanisms for allocating and deallocating memory • A special memory model • Abstraction to translate the need to use memory into a raw call for memory • Different allocator types represent different schemes for memory management • Very specialized purposes
Allocators • The STL includes several low-level algorithms for manipulating uninitialized memory • Default allocator • Default value everywhere an allocator can be used as an argument • Calls the new and delete operators namespace std { template <class T> class allocator; }
Using Allocators as an Application Programmer • Pass the allocator as a template argument • Examples: • A vector with special allocator MyAlloc<> std::vector<int,MyAlloc<int> > v; • An int/float map with special allocator std::map<int,float,less<int>, MyAlloc<std::pair<const int,float> > > m; • A string with special allocator std::basic_string<char,std::char_char_traits<char>, MyAlloc<char> > s;
Using Allocators as an Application Programmer • Special type definitions that use special allocators • Examples: • Special string type that uses special allocator typedef std::basic_string<char,std::char_char_traits<char>, MyAlloc<char> > xstring; • Special string/string map type that uses special allocator typedef std::map<xstring,xstring,less<xstring>, MyAlloc<std::pair<const xstring, xstring> > > xmap; • Create object of this type xmap mymap;
Using Allocators as an Application Programmer • No difference to use objects with other than the default allocator • Don’t mix elements with different allocators • Otherwise, the behavior is undefined • Can check whether two allocators use the same memory model if (mymap.get_allocator() == s.get_allocator()) { // OK, mymap and s use the same or interchangeable allocators … }
Using Allocators as a Library Programmer • Use allocators to implement containers and other components that are able to handle different allocators • With allocators, containers and algorithms can be parameterized by the way the elements are stored • Fundamental allocator operations
Using Allocators as a Library Programmer • An implementation of a vector • A vector gets its allocator as a template or a constructor argument and • stores it somewhere internally namespace std { template <class T, class Allocator = allocator<T> > class vector { private: Allocator alloc; // allocator T* elems; // array of elements size_type numElems; // number of elements size_type sizeElems; // size of memory for the elements …
Using Allocators as a Library Programmer public: explicit vector (const Allocator& Allocator()); explicit vector (size_type num, const T& val=T(), const Allocator& Allocator()); template <class InputIterator> vector(InputIterator beg, InputIterator end, const Allocator& Allocator()); vector(const vector<T, Allocator>& v); }; }
Using Allocators as a Library Programmer • Implementation of the second constructor • Initializes the vector by num elements of value val namespace std { template <class T, class Allocator> vector<T,Allocator>::vector(size_type num, const T& val, const Allocator& a) : alloc(a) { sizeElems = numElems = num; elems = alloc.allocate(num); for (size_type i = 0; i < num; ++i) alloc.construct(&elems[i], val); } }
Using Allocators as a Library Programmer • Convenience functions for uninitialized memory • These functions either succeed or have no effect
Utilities for Uninitialized Memory namespace std { template <class ForwIter, class T> void uninitialized_fill(ForwIter beg, ForwIter end, const T& value) { typedef typename iterator_traits<ForwIter>::value_type VT; ForwIter save(beg); try { for (; beg!=end; ++beg) new (static_cast<void*>(&*beg))VT(value); } catch (…) { for (; save!=beg; ++save) save->~VT(); throw; } } }
Utilities for Uninitialized Memory namespace std { template <class ForwIter, class Size, class T> void uninitialized_fill_n(ForwIter beg, Size num, const T& value) { typedef typename iterator_traits<ForwIter>::value_type VT; ForwIter save(beg); try { for (; num--; ++beg) new (static_cast<void*>(&*beg))VT(value); } catch (…) { for (; save!=beg; ++save) save->~VT(); throw; } } }
Utilities for Uninitialized Memory namespace std { template <class InputIter, class ForwIter> ForwIter uninitialized_copy(InputIter beg, InputIter end, ForwIter dest) { typedef typename iterator_traits<ForwIter>::value_type VT; ForwIter save(dest); try { for (; beg!=end; ++beg,++dest) new (static_cast<void*>(&*dest))VT(*beg); return dest; } catch (…) { for (; save!=beg; ++save) save->~VT(); throw; } } }
Using Allocators as a Library Programmer • Implementation of the second constructor • Using unitialized_fill_n() namespace std { template <class T, class Allocator> vector<T,Allocator>::vector(size_type num, const T& val, const Allocator& a) : alloc(a) { sizeElems = numElems = num; elems = alloc.allocate(num); unitialized_fill_n(elems, num, val); } }
Using Allocators as a Library Programmer • Implementation of reserve() namespace std { template <class T, class Allocator> void vector<T,Allocator>::reserve(size_type size) { if (size <= sizeElems) return; T* newmem = alloc.allocate(size); unitialized_copy(elems, elems+numElems, newmem); for (size_type i = 0; i < numElems; ++i) alloc.destroy(&elems[i]); alloc.deallocate(elems, sizeElems); sizeElems = size; elems = newmem; } }
Using Allocators as a Library Programmer • Raw storage iterators • Iterate over uninitialized memory to initialize it • First template argument (T*): output iterator for the type of the elements • Second template argument (T): the type of the elements • Initialize the storage to which elems refers by the value in range [x.begin(),x.end()) copy (x.begin(), x.end(), // source raw_storage_iterator<T*,T>(elems)); // destination
Default Allocator • Uses global operators new and delete to allocate and deallocate memory • allocate() may throw a bad_alloc exception • May be optimized • By reusing deallocated memory • By allocating more memory than needed to save time in additional allocations
Default Allocator • Constructors and destructor • Nothing to do because the allocator has no state • max_size() • Returns the largest value the can be passed meaningfully to allocate() to allocate storage • allocate() • Allocate memory with global new • deallocate() • Deallocate memory with global delete
Default Allocator namespace std { 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; };
Default Allocator pointer address (reference value) const { return &value; } const_pointer address (const_reference value) const { return &value; } allocator() throw() { } allocator(const allocator&) throw() { } template <class U> allocator (const allocator<U>&) throw() { } ~allocator() throw() { } size_type max_size () const throw() { return numeric_limits<size_t>::max() / sizeof(T); } pointer allocate (size_type num, allocator<void>::const_pointer hint = 0) { return (pointer)(::operator new(num*sizeof(T))); } void construct (pointer p, const T& value) { new((void*)p)T(value); }
Default Allocator void destroy (pointer p) { p->~T(); } void deallocate (pointer p, size_type num) { ::operator delete((void*)p); } }; // return that all specializations of this allocator are interchangeable template <class T1, class T2> bool operator== (const allocator<T1>&, const allocator<T2>&) throw() { return true; } template <class T1, class T2> bool operator!= (const allocator<T1>&, const allocator<T2>&) throw() { return false; } }
Default Allocator • Template structure rebind<> • Any allocator may allocate storage of another type indirectly • Allocator::rebind<T2>::other • The type of the same allocator specialized for elements of type T2 • rebind<> is useful for • implementing a container and • allocating memory for a type that differs from the element’s type
Default Allocator • Implementation of a deque namespace std { template <class T, class Allocator = allocator<T> > class deque { private: typedef typename Allocator::rebind<T*>::other PtrAllocator; Allocator alloc; // allocator for values of type T PtrAllocator block_alloc; // allocator for values of type T* T** elems; // array of blocks of elements … }; }
Default Allocator • Default has the specialization for type void namespace std { 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; }; }; }
A User-Defined Allocator • The only things that differ from the implementation of the default allocator • max_size() • allocate() • deallocate() • In these three functions, you program your own policy of memory allocation, such as • Reusing memory instead of freeing it immediately • Using share memory • Mapping the memory to a segment of an object-oriented database