BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlma_sequentialallocator

Detailed Description

Outline

Purpose

Provide a managed allocator using dynamically-allocated buffers.

Classes

See also
bdlma_infrequentdeleteblocklist, bdlma_sequentialpool

Description

This component provides a concrete mechanism, bdlma::SequentialAllocator, that implements the bdlma::ManagedAllocator protocol and efficiently allocates heterogeneous memory blocks (of varying, user-specified sizes) from a dynamically-allocated internal buffer:

,--------------------------.
`--------------------------'
| ctor/dtor
| allocateAndExpand
| reserveCapacity
| rewind
| truncate
V
,-----------------------.
( bdlma::ManagedAllocator )
`-----------------------'
| release
V
,----------------.
`----------------'
allocate
deallocate
Definition bdlma_sequentialallocator.h:279
Definition bslma_allocator.h:457

If an allocation request exceeds the remaining free memory space in the internal buffer, the allocator either replenishes its buffer with new memory to satisfy the request, or returns a separate memory block, depending on whether the request size exceeds an optionally-specified maximum buffer size. The release method releases all memory allocated through the allocator, as does the destructor. The rewind method releases all memory allocated through the allocator and returns to the underlying allocator only memory that was allocated outside of the typical internal buffer growth of the allocator (i.e., large blocks). Note that individually allocated memory blocks cannot be separately deallocated.

The main difference between a bdlma::SequentialAllocator and a bdlma::SequentialPool is that, very often, a bdlma::SequentialAllocator is managed through a bslma::Allocator pointer. Hence, every call to the allocate method invokes a virtual function call, which is slower than invoking the non-virtual allocate method on a bdlma::SequentialPool. However, since bslma::Allocator * is widely used across BDE interfaces, bdlma::SequentialAllocator is more general purpose than a bdlma::SequentialPool.

Optional initialSize Parameter

An optional initialSize parameter can be supplied at construction to specify the initial size of the internal buffer. If initialSize is not supplied, an implementation-defined value is used for the initial size of the internal buffer.

Optional maxBufferSize Parameter

If initialSize is specified, an optional maxBufferSize parameter can be supplied at construction to specify the maximum buffer size for geometric growth. Once the internal buffer grows up to the maxBufferSize, further requests that exceed this size will be served by a separate memory block instead of the internal buffer. The behavior is undefined unless maxBufferSize >= initialSize. Note that reserveCapacity always ensures that the requested number of bytes is available (allocating a new internal buffer if necessary) regardless of whether the size of the request exceeds maxBufferSize.

Optional growthStrategy Parameter

An optional growthStrategy parameter can be supplied at construction to specify the growth rate of the dynamically-allocated buffers. The buffers can grow either geometrically or remain constant in size. If growthStrategy is not specified, geometric growth is used. See bsls_blockgrowth for more details.

Optional alignmentStrategy Parameter

An optional alignmentStrategy parameter can be supplied at construction to specify the memory alignment strategy. Allocated memory blocks can either follow maximum alignment, natural alignment, or 1-byte alignment. If alignmentStrategy is not specified, natural alignment is used. See bsls_alignment for more details.

Usage

Allocators are often supplied, at construction, to objects requiring dynamically-allocated memory. For example, consider the following my_DoubleStack class whose constructor takes a bslma::Allocator *:

// my_doublestack.h
// ...
class my_DoubleStack {
// This class implements a stack that stores 'double' values.
// DATA
double *d_stack_p; // dynamically-allocated array
int d_size; // physical capacity of stack
int d_length; // next available index in stack
bslma::Allocator *d_allocator_p; // memory allocator (held, not owned)
private:
// PRIVATE MANIPULATORS
void increaseCapacity();
// Increase the capacity of this stack by at least one element.
// Not implemented:
my_DoubleStack(const my_DoubleStack&);
public:
// CREATORS
explicit my_DoubleStack(bslma::Allocator *basicAllocator = 0);
// Create a stack that stores 'double' values. Optionally specify
// a 'basicAllocator' used to supply memory. If 'basicAllocator'
// is 0, the currently installed default allocator is used.
~my_DoubleStack();
// Destroy this stack and all elements held by it.
// ...
// MANIPULATORS
void push(double value);
// Push the specified 'value' onto this stack.
// ...
};
// ...
// MANIPULATORS
inline
void my_DoubleStack::push(double value)
{
if (d_length >= d_size) {
increaseCapacity();
}
d_stack_p[d_length++] = value;
}
// ...
// my_doublestack.cpp
// PRIVATE MANIPULATORS
void my_DoubleStack::increaseCapacity()
{
// Implementation elided.
// ...
}
// CREATORS
my_DoubleStack::my_DoubleStack(bslma::Allocator *basicAllocator)
: d_size(1)
, d_length(0)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
d_stack_p = static_cast<double *>(
d_allocator_p->allocate(d_size * sizeof *d_stack_p));
}
Definition balxml_encoderoptions.h:68

Note that, when the allocator passed in is a bdlma::SequentialAllocator, the deallocate method is a no-op, and all memory is reclaimed during the destruction of the allocator:

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);
}
// ...

In main, users can create a bdlma::SequentialAllocator and pass it to the constructor of my_DoubleStack:

bdlma::SequentialAllocator sequentialAlloc;
my_DoubleStack dstack(&sequentialAlloc);