Quick Links: |
Provide a pure abstract interface for memory-allocation mechanisms. More...
Namespaces | |
namespace | bslma |
Functions | |
void * | operator new (std::size_t size, BloombergLP::bslma::Allocator &basicAllocator) |
void | operator delete (void *address, BloombergLP::bslma::Allocator &basicAllocator) |
void * | operator new (std::size_t size, BloombergLP::bslma::Allocator *basicAllocator) BSLS_KEYWORD_DELETED |
void | operator delete (void *address, BloombergLP::bslma::Allocator *basicAllocator) BSLS_KEYWORD_DELETED |
bslma::Allocator | protocol class for memory allocation and deallocation |
bslma_allocator
, that serves as a ubiquitous vocabulary type for various memory allocation mechanisms. The functional capabilities documented by this protocol are similar to those afforded by global operators new
and delete
: sufficiently (but not necessarily maximally) aligned memory is guaranteed for any object of a given size. Clients of this abstract base class will typically accept a supplied allocator (often at construction) and use its allocate
and deallocate
methods instead of new
and delete
directly. new
and delete
(see bslma_newdeleteallocator
): new
and delete
) in the derived class (see bslma_managedallocator
), we can bypass the individual destructors in a dynamically allocated type and remove all memory for one or more associated object almost instantly. bslma::Allocator
protocol, like any other protocol, isolates clients from direct coupling with platform level facilities that are not fully under their control. By installing a test allocator (see bslma_testallocator
), we are able to orchestrate the white-box testing of internal calls to global operators new
and delete
in a platform-neutral manner. bslma::Allocator
that are specifically designed for concurrent access must be documented as such. Unless specifically documented otherwise, separate objects of classes derived from bslma::Allocator
may safely be used in separate threads. bslma::Allocator
is an abstract class used to obtain "raw" memory of arbitrary size. A pool is a concrete data structure used to organize and supply memory according to specific needs (e.g., a consistent size). Concrete allocators may use pools in their implementations, and pools will always take a base bslma::Allocator
protocol in their interface. You can think of an allocator as a stream of memory that flows into a pool of memory. Memory is allocated from the pool until it is dry; only then does new memory flow into the pool from the allocator. new
to allow convenient syntax for the construction of objects using the bslma::Allocator
protocol. The overloaded new
operator defined in this component has a second parameter, bslma::Allocator&
, that identifies the concrete (derived) allocator that will be used to supply memory. #include <new>
) along with a bslma::Allocator
, used to allocate an arbitrary TYPE
. void someFunction(bslma::Allocator *basicAllocator) { TYPE *obj = new (basicAllocator->allocate(sizeof(TYPE))) TYPE(...); // ...
TYPE
throws an exception, the basicAllocator->deallocate
method is never called. new
, taking a reference to a modifiable bslma::Allocator
as an explicit argument allows for cleaner usage and guarantees that the basicAllocator->deallocate
method is called in case of an exception: void someFunction(bslma::Allocator *basicAllocator) { TYPE *obj = new (*basicAllocator) TYPE(...); // ...
delete
should not be called directly: The overloaded operator delete
supplied in this component is solely for the compiler to invoke in the event an exception is thrown during a failed construction. Instead, the bslma::Allocator
protocol provides deleteObject
(a template member function parameterized by the type of the object being deleted), which is implemented conceptually as follows: template <class TYPE> void bslma::Allocator::deleteObject(TYPE *address) { address->~TYPE(); this->deallocate(address); }
deleteObjectRaw
which is more efficient when it is known that the address
does not refer to a secondary base class of the object being deleted. bslma::Allocator
protocol provided in this component defines a bilateral contract between suppliers and consumers of raw memory. The following subsections illustrate (1) use, and (2) implementation of the abstract bslma::Allocator
base class: my_DoubleStack
class, parameterized by a bslma::Allocator
: // my_doublestack.h // ... namespace bslma { class Allocator; } // forward declaration of allocator class my_DoubleStack { // DATA double *d_stack_p; // dynamically allocated array (d_size // elements) int d_size; // physical capacity of this stack (in // elements) int d_length; // logical index of next available // stack element bslma::Allocator *d_allocator_p; // holds (but does not own) object // FRIENDS friend class my_DoubleStackIter; private: // PRIVATE MANIPULATORS void increaseSize(); // Increase the capacity by at least one element. public: // CREATORS my_DoubleStack(bslma::Allocator *basicAllocator = 0); my_DoubleStack(const my_DoubleStack& other, bslma::Allocator *basicAllocator = 0); ~my_DoubleStack(); // MANIPULATORS my_DoubleStack& operator=(const my_DoubleStack& rhs); void push(double value); void pop(); // ACCESSORS const double& top() const; bool isEmpty() const; }; // ... inline void my_DoubleStack::push(double value) { if (d_length >= d_size) { increaseSize(); } d_stack_p[d_length++] = item; } // ...
basicAllocator
supplied only at construction. (We avoid use of the name allocator
so as not to conflict with the STL use of the word, which differs slightly.) If non-zero, the stack holds a pointer to this allocator, but does not own it. If no allocator is supplied, the implementation itself must either conditionally invoke global new
and delete
explicitly whenever dynamic memory must be managed (BAD IDEA) or (GOOD IDEA) install a default allocator that adapts use of these global operators to the bslma_allocator
interface (see bslma_newdeleteallocator
). // my_doublestack.cpp #include <my_doublestack.h> #include <bslma_allocator.h> #include <bslma_newdeleteallocator.h> // adapter for 'new' and 'delete' // ... enum { INITIAL_SIZE = 1, GROW_FACTOR = 2 }; // ... // CREATORS my_DoubleStack::my_DoubleStack(bslma::Allocator *basicAllocator) : d_size(INITIAL_SIZE) , d_length(0) , d_allocator_p(bslma::NewDeleteAllocator::allocator(basicAllocator)) // The above initialization expression is equivalent to 'basicAllocator // ? basicAllocator : &bslma::NewDeleteAllocator::singleton()'. { assert(d_allocator_p); d_stack_p = (double *) d_allocator_p->allocate(d_size * sizeof *d_stack_p); } my_DoubleStack::~my_DoubleStack() { // CLASS INVARIANTS assert(d_allocator_p); assert(d_stack_p); assert(0 <= d_length); assert(0 <= d_size); assert(d_length <= d_size); d_allocator_p->deallocate(d_stack_p); }
.cpp
file. Subsequent use of the allocator is demonstrated by the following file-scope static reallocation function: static inline void reallocate(double **array, int newSize, int length, bslma::Allocator *basicAllocator) // Reallocate memory in the specified 'array' to the specified // 'newSize' using the specified 'basicAllocator'. The specified // 'length' number of leading elements are preserved. Since the // class invariant requires that the physical capacity of the // container may grow but never shrink, the behavior is undefined // unless 'length <= newSize'. { assert(array); assert(1 <= newSize); assert(0 <= length); assert(basicAllocator); assert(length <= newSize); // enforce class invariant double *tmp = *array; // support exception neutrality *array = (double *) basicAllocator->allocate(newSize * sizeof **array); // COMMIT POINT std::memcpy(*array, tmp, length * sizeof **array); basicAllocator->deallocate(tmp); } // PRIVATE MANIPULATORS void my_DoubleStack::increaseSize() { int proposedNewSize = d_size * GROW_FACTOR; // reallocate can throw assert(proposedNewSize > d_length); reallocate(&d_stack_p, proposedNewSize, d_length, d_allocator_p); d_size = proposedNewSize; // we're committed }
bslma::Allocator
interface to be useful, we must supply a concrete allocator that implements it. In this example we demonstrate how to adapt operator new
and operator delete
to this protocol base class. // my_newdeleteallocator.h // ... class my_NewDeleteAllocator : public bslma::Allocator { // This class is a sample concrete implementation of the // 'bslma::Allocator' protocol that provides direct access to the // system-supplied (native) global operators 'new' and 'delete'. // NOT IMPLEMENTED my_NewDeleteAllocator(const bslma::NewDeleteAllocator&); my_NewDeleteAllocator& operator=(const bslma::NewDeleteAllocator&); public: // CREATORS my_NewDeleteAllocator(); // Create an allocator that wraps the global (native) operators // 'new' and 'delete' to supply memory. Note that all objects of // this class share the same underlying resource. virtual ~my_NewDeleteAllocator(); // Destroy this allocator object. Note that destroying this // allocator has no effect on any outstanding allocated memory. // MANIPULATORS virtual void *allocate(size_type size); // Return a newly allocated block of memory of (at least) the // specified positive 'size' (in bytes). If 'size' is 0, a null // pointer is returned with no other effect. If this allocator // cannot return the requested number of bytes, then it will throw // a 'std::bad_alloc' exception in an exception-enabled build, or // else will abort the program in a non-exception build. The // behavior is undefined unless '0 <= size'. Note that the // alignment of the address returned is the maximum alignment for // any type defined on this platform. Also note that global // 'operator new' is *not* called when 'size' is 0 (in order to // avoid having to acquire a lock, and potential contention in // multi-threaded programs). virtual void deallocate(void *address); // Return the memory block at the specified 'address' back to this // allocator. If 'address' is 0, this function has no effect. The // behavior is undefined unless 'address' was allocated using this // allocator object and has not already been deallocated. Note // that global 'operator delete' is *not* called when 'address' is // 0 (in order to avoid having to acquire a lock, and potential // contention in multi-treaded programs). }; // CREATORS inline my_NewDeleteAllocator::my_NewDeleteAllocator() { } // ...
my_NewDeleteAllocator
are defined in the component .cpp
file as they would not be inlined when invoked from the base class, which would be the typical usage in this case: // my_newdeleteallocator.cpp #include <my_newdeleteallocator.h> // CREATORS my_NewDeleteAllocator::~my_NewDeleteAllocator() { } // MANIPULATORS void *my_NewDeleteAllocator::allocate(size_type size) { BSLS_ASSERT_SAFE(0 <= size); return 0 == size ? 0 : ::operator new(size); } void my_NewDeleteAllocator::deallocate(void *address) { // While the C++ standard guarantees that calling delete(0) is safe // (3.7.3.2 paragraph 3), some libc implementations take out a lock to // deal with the free(0) case, so this check can improve efficiency of // threaded programs. if (address) { ::operator delete(address); } }
void* operator new | ( | std::size_t | size, | |
BloombergLP::bslma::Allocator & | basicAllocator | |||
) | [inline] |
Return the memory allocated from the specified basicAllocator
of at least the specified size
bytes, or 0 if size
is 0. The behavior is undefined unless 0 <= static_cast<bslma::Allocator::size_type>(size)
. Note that an object may allocate additional memory internally, requiring the allocator to be passed in as a constructor argument:
my_Type *createMyType(bslma::Allocator *basicAllocator) { return new (*basicAllocator) my_Type(..., basicAllocator); }
Note also that the analogous version of operator delete
should not be called directly. Instead, this component provides a template member function deleteObject
parameterized by TYPE
that effectively performs the following operations:
void deleteMyType(bslma::Allocator *basicAllocator, my_Type *address) { address->~my_Type(); basicAllocator->deallocate(address); }
See also deleteObjectRaw
for better performance when address
is known not to be a secondary base type of the object being deleted.
void operator delete | ( | void * | address, | |
BloombergLP::bslma::Allocator & | basicAllocator | |||
) | [inline] |
Use the specified basicAllocator
to deallocate the memory at the specified address
. The behavior is undefined unless address
was allocated using basicAllocator
and has not already been deallocated. This operator is supplied solely to allow the compiler to arrange for it to be called in case of an exception.
void* operator new | ( | std::size_t | size, | |
BloombergLP::bslma::Allocator * | basicAllocator | |||
) |
Note that this operator is intentionally not defined.
void operator delete | ( | void * | address, | |
BloombergLP::bslma::Allocator * | basicAllocator | |||
) |
Note that this operator is intentionally not defined.