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

Detailed Description

Outline

Purpose

Provide a proctor to conditionally unwind memory block allocation.

Classes

See also
bslma_deallocateobjectproctor, bslma_deleteobjectproctor

Description

This component provides a proctor class template, bslma::DeallocateBytesProctor, that conditionally reverses the effect of bslma::AllocatorUtil::allocateBytes(a, n, align) or a->allocate(n), where a is a memory resource or pool that allocates, n is the number of bytes to allocate, and align is the desired alignment. Upon destruction, this proctor deallocates the block from the specified allocator or pool without calling any destructors. The proctor's constructor takes the same arguments as bslma::AllocatorUtil::deallocateBytes, making it straightforward to allocate an object using allocateBytes and protect it using DeallocateBytesProctor.

As with all proctors, this class is used to automatically reclaim a resource in the event that a function ends prematurely, e.g., via an exception. After allocating a block of memory, an exception-safe function would create a DeallocateBytesProctor to manage the new memory block. If the operation completes successfully, the code calls the proctor's release method, which disengages the proctor. If, however, the function exits while the proctor is still engaged, the proctor's destructor will deallocate the managed memory block.

The DeallocateBytesProctor template has one template parameter: the ALLOCATOR type used to reclaim storage managed by the proctor. If ALLOCATOR is a non-pointer type, the proctor uses bslma::AllocatorUtil::deallocateBytes(a, ptr, n, align) to reclaim storage, where a is the allocator, ptr is the address of the managed memory block, n is the number of managed bytes, and align is an optional argument specifying the alignment of the block (max-aligned by default). Otherwise, if ALLOCATOR is a pointer type, the proctor reclaims memory by calling a->deallocate(ptr). Instantiating DeallocateBytesProctor with a pointer-type ALLOCATOR is appropriate for classes derived from bslma::Allocator and for BDE-style pool types, i.e., classes for which a->deallocate(ptr) is well formed.

Usage

Example 1: Class having an owning pointer


In this example, we create a class, my_Manager, having an owning pointer to an object of another class, my_Data. Because it owns the my_Data object, my_Manager is responsible for allocating, constructing, deallocating, and destroying it.

First, we define the my_Data class, which holds an integer value and counts how many times its constructor and destructor have been called. Its constructor will throw an exception if its integer argument equals the number of constructor calls before construction:

class my_Data {
// DATA
int d_value;
// CLASS DATA
static int s_numConstructed;
static int s_numDestroyed;
public:
// CLASS METHODS
static int numConstructed() { return s_numConstructed; }
static int numDestroyed() { return s_numDestroyed; }
// CREATORS
explicit my_Data(int v) : d_value(v)
{
if (v == s_numConstructed) throw s_numConstructed;
++s_numConstructed;
}
my_Data(const my_Data& original);
~my_Data() { ++s_numDestroyed; }
};
int my_Data::s_numConstructed = 0;
int my_Data::s_numDestroyed = 0;

Next, we define my_Manager as an allocator-aware class holding a pointer to my_Data and maintaining its own count of constructor invocations:

class my_Manager {
// DATA
my_Data *d_data_p;
// CLASS DATA
static int s_numConstructed;
public:
// TYPES
typedef bsl::allocator<> allocator_type;
// CLASS METHODS
static int numConstructed() { return s_numConstructed; }
// CREATORS
explicit my_Manager(int v,
const allocator_type& allocator = allocator_type());
my_Manager(const my_Manager& original);
~my_Manager();
// ...
};
int my_Manager::s_numConstructed = 0;
Definition bslma_bslallocator.h:580

Next, we define the constructor for my_Manager, which begins by allocating a my_Data object:

my_Manager::my_Manager(int v, const allocator_type& allocator)
: d_allocator(allocator), d_data_p(0)
{
d_data_p = static_cast<my_Data*>(
bslma::AllocatorUtil::allocateBytes(allocator, sizeof(my_Data)));
static AllocatorUtil_Traits< t_ALLOCATOR >::void_pointer allocateBytes(const t_ALLOCATOR &allocator, std::size_t nbytes, std::size_t alignment=k_MAX_ALIGNMENT)
Definition bslma_allocatorutil.h:871

Then, the my_Manager constructor constructs the my_Data object in the allocated memory. However, as the constructor might throw, it first protects the data object with a bslma::DeallocateBytesProctor:

proctor(d_allocator, d_data_p, sizeof(my_Data));
bslma::ConstructionUtil::construct(d_data_p, d_allocator, v);
Definition bslma_deallocatebytesproctor.h:272
static void construct(TARGET_TYPE *address, const ALLOCATOR &allocator)
Definition bslma_constructionutil.h:1243

Then, once the construct operation completes successfully, we can release the data object from the proctor. Only then do we increment the construction count, as the constructor is now complete:

proctor.release();
++s_numConstructed;
}

Next, we define the my_Manager destructor, which destroys and deallocates its data object. Note that the arguments to deallocateBytes is identical to the constructor arguments to the DeallocateBytesProctor, above:

my_Manager::~my_Manager()
{
d_data_p->~my_Data();
sizeof(my_Data));
}
static void deallocateBytes(const t_ALLOCATOR &allocator, typename AllocatorUtil_Traits< t_ALLOCATOR >::void_pointer p, std::size_t nbytes, std::size_t alignment=k_MAX_ALIGNMENT)
Definition bslma_allocatorutil.h:911

Now, we use a bslma::TestAllocator to verify that, under normal (non exceptional) circumstances, constructing a my_Manager object will result in one block of memory being allocated and one invocation of the my_Data constructor:

int main()
{
{
my_Manager obj1(7, &ta);
assert(1 == ta.numBlocksInUse());
assert(1 == ta.numBlocksTotal());
assert(1 == my_Manager::numConstructed());
}
assert(0 == ta.numBlocksInUse());
assert(1 == ta.numBlocksTotal());
assert(1 == my_Manager::numConstructed());
Definition bslma_testallocator.h:384
bsls::Types::Int64 numBlocksInUse() const
Definition bslma_testallocator.h:1087
bsls::Types::Int64 numBlocksTotal() const
Definition bslma_testallocator.h:1099

Finally, when the my_Data constructor does throw, a block is allocated but we verify that the my_Manager constructor did not complete and that the block is automatically deallocated, resulting in no leaks:

try {
my_Manager obj2(1, &ta); // Will throw an exception
assert(false && "Can't get here");
}
catch (int e) {
assert(1 == e);
assert(2 == ta.numBlocksTotal()); // A 2nd block was allocated...
assert(0 == ta.numBlocksInUse()); // ...but was then deallocated
assert(1 == my_Manager::numConstructed());
}
assert(1 == my_Manager::numConstructed());
}