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

Macros

#define bslma_DeallocatorProctor   bslma::DeallocatorProctor
 This alias is defined for backward compatibility.
 

Detailed Description

Outline

Purpose

Provide a proctor to conditionally manage a block memory.

Classes

See also
bslma_deallocatorguard, bslma_autodeallocator

Description

This component provides a proctor class template, bslma::DeallocatorProctor, to conditionally manage a block of (otherwise-unmanaged) memory. If not explicitly released, the managed memory is deallocated automatically when the proctor object goes out of scope by freeing the memory using the parameterized ALLOCATOR (allocator or pool) supplied at construction. Note that after a proctor object releases its managed memory, the same proctor can be reused to conditionally manage another block of memory (allocated from the same allocator or pool that was supplied at construction) by invoking the reset method.

Requirement

The parameterized ALLOCATOR type of the bslma::DeallocatorProctor class must provide a (possibly virtual) method:

void deallocate(void *address);

to deallocate memory at the specified address (originally supplied by the ALLOCATOR object).

Usage

The bslma::DeallocatorProctor is normally used to achieve exception safety in an exception neutral way by managing memory in a sequence of continuous memory allocations. Since each memory allocation may potentially throw an exception, an object of this proctor class can be used to (temporarily) manage newly allocated memory while attempting to allocate additional memory. Should an exception occur in subsequent memory allocation, the proctor's destructor deallocates its managed memory, preventing a memory leak.

This example illustrate a typical use of bslma::DeallocatorProctor. Suppose we have an array class that stores an "in-place" representation of objects of parameterized TYPE:

// my_array.h
// ...
/// This class implements an "in-place" array of objects of
/// parameterized `TYPE` stored contiguously in memory.
template <class TYPE>
class my_Array {
// DATA
TYPE *d_array_p; // dynamically allocated array
int d_length; // logical length of this array
int d_size; // physical capacity of this array
bslma::Allocator *d_allocator_p; // allocator (held, not owned)
public:
// CREATORS
/// Create a `my_Array` object. Optionally specify a
/// `basicAllocator` used to supply memory. If `basicAllocator` is
/// 0, the currently installed default allocator is used.
my_Array(bslma::Allocator *basicAllocator = 0);
// ...
/// Destroy this `my_Array` object and all elements currently
/// stored.
~my_Array();
// MANIPULATORS
// ...
/// Append (a copy of) the specified `object` of parameterized
/// `TYPE` to (the end of) this array.
void append(const TYPE& object);
// ...
};
Definition bslma_allocator.h:457

Note that the rest of the my_Array interface (above) and implementation (below) is elided as the portion shown is sufficient to demonstrate the use of bslma::DeallocatorProctor.

// CREATORS
template <class TYPE>
inline
my_Array<TYPE>::my_Array(bslma::Allocator *basicAllocator)
: d_length(0)
, d_size(1)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
d_array_p = (TYPE *)d_allocator_p->allocate(sizeof(TYPE));
}
template <class TYPE>
my_Array<TYPE>::~my_Array()
{
for (int i = 0; i < d_length; ++i) {
d_array_p[i].~TYPE();
}
d_allocator_p->deallocate(d_array_p);
}
Definition balxml_encoderoptions.h:68

In order to implement the append function, we first have to introduce an my_AutoDestructor class, which automatically destroy a sequence of managed objects upon destruction. See bslma::AutoDestructor for a similar component with full documentation:

// my_autodestructor.h
// ...
/// This class implements a range proctor that, unless its `release`
/// method has previously been invoked, automatically invokes the
/// destructor of each of sequence of objects it manages.
template <class TYPE>
class my_AutoDestructor {
// DATA
TYPE * d_origin_p;
int d_length;
public:
// CREATORS
/// Create an `my_AutoDestructor` to manage a contiguous sequence of
/// objects.
my_AutoDestructor(TYPE *origin, int length)
: d_origin_p(origin)
, d_length(length)
{
}
/// Destroy this `my_AutoDestructor` and, unless its `release`
/// method has previously been invoked, destroy the sequence of
/// objects it manages by invoking the destructor of each of the
/// (managed) objects.
~my_AutoDestructor()
{
if (d_length) {
for (; d_length > 0; --d_length, ++d_origin_p) {
d_origin_p->~TYPE();
}
}
}
// MANIPULATORS
/// Increase by one the length of the sequence of objects managed by
/// this range proctor.
my_AutoDestructor<TYPE>& operator++()
{
++d_length;
return *this;
}
/// Release from management the sequence of objects currently
/// managed by this range proctor.
void release()
{
d_length = 0;
}
};
FunctionOutputIterator< FUNCTION > & operator++(FunctionOutputIterator< FUNCTION > &iterator)
Do nothing and return specified iterator.
Definition bdlb_functionoutputiterator.h:405

We can now continue with our implementation of the my_Array class:

// my_array.h
// ...
// MANIPULATORS
template <class TYPE>
void my_Array<TYPE>::append(const TYPE &object)
{
if (d_length == d_size) {
TYPE *newArray = (TYPE *)d_allocator_p->allocate(
d_size * 2 * sizeof(TYPE)); // possibly throw
//*****************************************************************
// Note the use of the deallocator proctor on 'newArray' (below). *
//*****************************************************************
d_allocator_p);
// Note use of 'my_AutoDestructor' here to protect the copy
// construction of 'TYPE' objects.
my_AutoDestructor<TYPE> destructor(newArray, 0);
for (int i = 0; i < d_length; ++i) {
new(&newArray[i]) TYPE(d_array_p[i], d_allocator_p);
// possibly throw
d_array_p[i].~TYPE();
++destructor;
}
destructor.release();
//*********************************************************
// Note that the deallocator proctor is released (below). *
//*********************************************************
proctor.release();
d_allocator_p->deallocate(d_array_p);
d_array_p = newArray;
d_size *= 2;
}
new(&d_array_p[d_length]) TYPE(object, d_allocator_p);
++d_length;
}
Definition bslma_deallocatorproctor.h:312

Both the use of bslma::DeallocatorProctor and my_AutoDestructor are necessary to implement exception safety.

The append method defined above potentially throws in two places. If the memory allocator held in d_allocator_p where to throw while attempting to allocate the new array of parameterized TYPE, no memory would be leaked. But without subsequent use of the bslma::DeallocatorProctor, if the allocator subsequently throws while copy constructing the objects from the old array to the new array, the newly allocated memory block would be leaked. Using the bslma::DeallocatorProctor prevents the leak by deallocating the proctored memory automatically should the proctor go out of scope before the release method of the proctor is called (such as when the function exits prematurely due to an exception).

Similarly, any resources acquired as a result of copy constructing the objects from the old array to the new array would be leaked if the constructor of TYPE throws. Using the my_AutoDestructor prevents the leak by invoking the destructor of the proctored (and newly created) objects in the new array should the my_AutoDestructor goes out of scope before the release method of the proctor is called.

Note that the append method assumes the copy constructor of TYPE takes an allocator as a second argument. In production code, a constructor proxy that checks the traits of TYPE (to see whether TYPE uses bslma::Allocator) should be used (see bslalg::ConstructorProxy).

Macro Definition Documentation

◆ bslma_DeallocatorProctor

#define bslma_DeallocatorProctor   bslma::DeallocatorProctor