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

Detailed Description

Outline

Purpose

Provide a wrapper for STL allocators, for container use.

Classes

See also
bslma_bslallocator

Description

This component provides a single mechanism class, bslalg::ContainerBase, that can used as a base class by STL-style containers for storing the container's allocator. If instantiated with an empty allocator type, ContainerBase will itself be an empty class type. Thus, a container class derived from ContainerBase can benefit from the base-class optimization in that an empty allocator object will not add to the size of the container's footprint.

Usage

This section illustrates intended use of this component.

Example 1: Creating a Fixed-Size Array with bslalg::ContainerBase

Suppose we would like to implement a fixed-size array that allocates memory from a user-supplied allocator at construction.

First, we define the interface of the container, MyFixedSizeArray. We privately derive from ContainerBase to take advantage of the empty-base-class optimization (in case ALLOCATOR is an empty class) and to take advantage of implementation conveniences ContainerBase provides:

#include <bslmf_isempty.h>
/// This class implements a container that contains a fixed number of
/// elements of the parameterized type `VALUE` using the parameterized
/// `ALLOCATOR` to allocate memory. The number of elements is specified
/// on construction.
template <class VALUE, class ALLOCATOR>
class MyFixedSizeArray : private bslalg::ContainerBase<ALLOCATOR>
{
// PRIVATE TYPES
// DATA
VALUE *d_array; // head pointer to the array of elements
const int d_size; // (fixed) number of elements in 'd_array'
public:
// TYPES
typedef ALLOCATOR allocator_type;
// CREATORS
/// Create a `MyFixedSizeArray` object having the specified `size`
/// elements, and using the optionally specified `allocator` to
/// supply memory. Each element of the array is value initialized.
explicit MyFixedSizeArray(int size,
const ALLOCATOR& allocator = ALLOCATOR());
/// Create a `MyFixedSizeArray` object having same number of
/// elements as that of the specified `original`, the same value of
/// each element as that of corresponding element in `original`, and
/// using the optionally specified `allocator` to supply memory.
MyFixedSizeArray(const MyFixedSizeArray& original,
const ALLOCATOR& allocator = ALLOCATOR());
/// Destroy this object.
~MyFixedSizeArray();
// MANIPULATORS
/// Return a modifiable reference to the specified `i`th element of
/// this object. The behavior is undefined unless `i < size()`.
VALUE& operator[](int i) { return d_array[i]; }
// ACCESSORS
/// Return a const reference to the specified `i`th element of this
/// object. The behavior is undefined unless `i < size()`.
const VALUE& operator[](int i) const { return d_array[i]; }
/// Return the allocator used by this object to allocate memory.
ALLOCATOR get_allocator() const;
/// Return the number of elements contained in this object.
int size() const { return d_size; }
};
Definition bslalg_containerbase.h:386
bsl::size_t size(const TYPE &array)
Return the number of elements in the specified array.

Next, we define the get_allocator accessor, which extracts the allocator from the ContainerBase base class using its allocatorRef method:

template<class VALUE, class ALLOCATOR>
inline
ALLOCATOR
MyFixedSizeArray<VALUE,ALLOCATOR>::get_allocator() const {
return Base::allocatorRef();
}

Next, we define the first constructor, beginning with the initialization the ContainerBase base class with the supplied allocator:

template<class VALUE, class ALLOCATOR>
MyFixedSizeArray<VALUE,ALLOCATOR>::MyFixedSizeArray(
int size,
const ALLOCATOR& allocator)
: Base(allocator)
, d_size(size)
{

Then, we allocate the specified number of array elements using the allocator returned by the get_allocator() method. Once allocated, we protect the array memory with a bslma::DeallocateObjectProctor object:

d_array =
bslma::AllocatorUtil::allocateObject<VALUE>(get_allocator(),
d_size);
deallocateProctor(get_allocator(), d_array, d_size);
Definition bslma_deallocateobjectproctor.h:273

Then, we invoke the constructor for each array element using the bslma::ConstructionUtil::construct method. We use a bslma::AutoDestuctor proctor to unwind these constructions if an exception is thrown:

bslma::AutoDestructor<VALUE> autoDtor(d_array, 0);
// Default construct each element of the array:
for (int i = 0; i < d_size; ++i) {
bslma::ConstructionUtil::construct(&d_array[i], get_allocator());
++autoDtor;
}
Definition bslma_autodestructor.h:283
static void construct(TARGET_TYPE *address, const ALLOCATOR &allocator)
Definition bslma_constructionutil.h:1243

Then, when every element has been constructed, we free the proctors:

autoDtor.release();
deallocateProctor.release();
}

Next we implement the destructor as the reverse of the constructor, invoking bslma::DestructionUtil::destroy on each element then deallocating them with bslma::AllocatorUtil::deallocateObject:

template<class VALUE, class ALLOCATOR>
MyFixedSizeArray<VALUE,ALLOCATOR>::~MyFixedSizeArray()
{
// Call destructor for each element
for (int i = 0; i < d_size; ++i) {
bslma::DestructionUtil::destroy(&d_array[i]);
}
// Return memory to allocator.
d_array, d_size);
}
static void deallocateObject(const t_ALLOCATOR &allocator, t_POINTER p, std::size_t n=1)
Definition bslma_allocatorutil.h:926

Next, for testing purposes, we create a StatelessAllocator template that simply allocates a global test allocator:

bslma::TestAllocator g_testAllocator;
/// Allocator that allocates from the default `bslma::Allocator` resource.
template <class TYPE>
class StatelessAllocator {
public:
typedef TYPE value_type;
value_type *allocate(std::size_t n, void * = 0) {
return bslma::AllocatorUtil::allocateObject<value_type>(
&g_testAllocator, n);
}
void deallocate(value_type *p, std::size_t n) {
bslma::AllocatorUtil::deallocateObject(&g_testAllocator, p, n);
}
};
Definition bslma_testallocator.h:384

Finally, we create two MyFixedSizeArray objects, one using StatelessAllocator, and the other using bsl::allocator, and we verify that memory is allocated from the correct allocator for each. Because StatelessAllocator is an empty class, the first object is smaller than the second object by at least the size of a bsl::allocator.

int main()
{
assert(bsl::is_empty<StatelessAllocator<int> >::value);
MyFixedSizeArray<int, StatelessAllocator<int> > fixedArray1(3);
assert(3 == fixedArray1.size());
assert(1 == g_testAllocator.numBlocksInUse());
assert(3 * sizeof(int) == g_testAllocator.numBytesInUse());
MyFixedSizeArray<int, bsl::allocator<int> > fixedArray2(3, &ta);
assert(3 == fixedArray2.size());
assert(&ta == fixedArray2.get_allocator());
assert(1 == ta.numBlocksInUse());
assert(3 * sizeof(int) == ta.numBytesInUse());
assert(sizeof(fixedArray2) - sizeof(fixedArray1) >=
}
Definition bslma_bslallocator.h:580
bsls::Types::Int64 numBlocksInUse() const
Definition bslma_testallocator.h:1087
bsls::Types::Int64 numBytesInUse() const
Definition bslma_testallocator.h:1111
Definition bslmf_isempty.h:315