Provide a managed allocator using dynamically-allocated buffers.
More...
Namespaces |
namespace | bdlma |
Detailed Description
- Outline
-
-
- Purpose:
- Provide a managed allocator using dynamically-allocated buffers.
-
- Classes:
-
- See also:
- Component bdlma_infrequentdeleteblocklist, Component 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: ,--------------------------.
( bdlma::SequentialAllocator )
`--------------------------'
| ctor/dtor
| allocateAndExpand
| reserveCapacity
| rewind
| truncate
V
,-----------------------.
( bdlma::ManagedAllocator )
`-----------------------'
| release
V
,----------------.
( bslma::Allocator )
`----------------'
allocate
deallocate
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 *
:
class my_DoubleStack {
double *d_stack_p;
int d_size;
int d_length;
bslma::Allocator *d_allocator_p;
private:
void increaseCapacity();
my_DoubleStack(const my_DoubleStack&);
public:
explicit my_DoubleStack(bslma::Allocator *basicAllocator = 0);
~my_DoubleStack();
void push(double value);
};
inline
void my_DoubleStack::push(double value)
{
if (d_length >= d_size) {
increaseCapacity();
}
d_stack_p[d_length++] = value;
}
void my_DoubleStack::increaseCapacity()
{
}
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));
}
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()
{
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
: