Release memory to a managed allocator or pool at destruction.
More...
Namespaces |
namespace | bdlma |
Detailed Description
- Outline
-
-
- Purpose:
- Release memory to a managed allocator or pool at destruction.
-
- Classes:
-
- See also:
- Component bslma_deallocatorproctor, Component bdlma_managedallocator
-
- Description:
- This component provides a proctor object,
bdlma::AutoReleaser
, to manage memory allocated from a managed allocator or pool. The proctor's destructor invokes the release
method of its managed allocator or pool unless the proctor's own release
method has been called. Note that after a proctor releases management of its managed allocator or pool, the proctor can be reused by invoking its reset
method with another allocator or pool object (of the same (template parameter) type ALLOCATOR
).
-
- Requirements:
- The object of the (template parameter) type
ALLOCATOR
must provide a method having the following signature:
-
- Usage:
- This section illustrates intended use of this component.
-
- Example 1: Using a bdlma::AutoReleaser to Preserve Exception Neutrality:
- A
bdlma::AutoReleaser
proctor is often used to preserve exception neutrality for containers that allocate their elements using a managed allocator or pool. For operations that may potentially throw an exception, a proctor can be used to (temporarily) manage the container's allocator or pool and its associated memory. If an exception is thrown, the proctor's destructor invokes the release
method of its held allocator or pool, deallocating memory for all of the container's elements, thereby preventing a memory leak and restoring the container to the empty state.
- In this example, we illustrate use of a
bdlma::AutoReleaser
proctor within the operator=
method of my_FastStrArray
, a class that implements an array of C string elements. Note that a my_FastStrArray
object allocates memory for its C string elements using a string pool, my_StrPool
, the definition of which is elided.
- First, we define the interface of our
my_FastStrArray
class: class my_FastCstrArray {
char **d_array_p;
int d_capacity;
int d_length;
my_StrPool d_strPool;
bslma::Allocator *d_allocator_p;
private:
void increaseSize();
my_FastCstrArray(const my_FastCstrArray&);
public:
my_FastCstrArray(bslma::Allocator *basicAllocator = 0);
~my_FastCstrArray();
my_FastCstrArray& operator=(const my_FastCstrArray& rhs);
void append(const char *item);
const char *operator[](int index) const { return d_array_p[index]; }
int length() const { return d_length; }
};
ostream& operator<<(ostream& stream, const my_FastCstrArray& array);
Then, we implement the methods: enum {
k_MY_INITIAL_SIZE = 1,
k_MY_GROW_FACTOR = 2
};
static inline
int nextSize(int size)
{
return size * k_MY_GROW_FACTOR;
}
static inline
void reallocate(char ***array,
int *size,
int newSize,
int length,
bslma::Allocator *allocator)
{
ASSERT(array);
ASSERT(*array);
ASSERT(size);
ASSERT(1 <= newSize);
ASSERT(0 <= length);
ASSERT(length <= *size);
ASSERT(length <= newSize);
char **tmp = *array;
*array = (char **)allocator->allocate(newSize * sizeof **array);
bsl::memcpy(*array, tmp, length * sizeof **array);
*size = newSize;
allocator->deallocate(tmp);
}
void my_FastCstrArray::increaseSize()
{
reallocate(&d_array_p,
&d_capacity,
nextSize(d_capacity),
d_length,
d_allocator_p);
}
my_FastCstrArray::my_FastCstrArray(bslma::Allocator *basicAllocator)
: d_capacity(k_MY_INITIAL_SIZE)
, d_length(0)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
d_array_p = (char **)d_allocator_p->allocate(
d_capacity * sizeof *d_array_p);
}
my_FastCstrArray::~my_FastCstrArray()
{
ASSERT(1 <= d_capacity);
ASSERT(0 <= d_length);
ASSERT(d_length <= d_capacity);
d_allocator_p->deallocate(d_array_p);
}
Now, we implement my_FastCstrArray::operator=
using a bdlma::AutoReleaser
proctor to preserve exception neutrality:
my_FastCstrArray&
my_FastCstrArray::operator=(const my_FastCstrArray& rhs)
{
if (&rhs != this) {
d_strPool.release();
d_length = 0;
if (rhs.d_length > d_capacity) {
char **tmp = d_array_p;
d_array_p = (char **)d_allocator_p->allocate(
rhs.d_length * sizeof *d_array_p);
d_capacity = rhs.d_length;
d_allocator_p->deallocate(tmp);
}
bdlma::AutoReleaser<my_StrPool> autoReleaser(&d_strPool);
for (int i = 0; i < rhs.d_length; ++i) {
const int size =
static_cast<int>(bsl::strlen(rhs.d_array_p[i])) + 1;
d_array_p[i] = (char *)d_strPool.allocate(size);
bsl::memcpy(d_array_p[i], rhs.d_array_p[i], size);
}
d_length = rhs.d_length;
autoReleaser.release();
}
return *this;
}
Note that a bdlma::AutoReleaser
proctor is used to manage the array's C string memory pool while allocating memory for the individual elements. If an exception is thrown during the for
loop, the proctor's destructor releases memory for all elements allocated through the pool, thus ensuring that no memory is leaked.
- Finally, we complete the implementation:
void my_FastCstrArray::append(const char *item)
{
if (d_length >= d_capacity) {
this->increaseSize();
}
const int sSize = static_cast<int>(bsl::strlen(item)) + 1;
char *elem = (char *)d_strPool.allocate(sSize);
bsl::memcpy(elem, item, sSize * sizeof *item);
d_array_p[d_length] = elem;
++d_length;
}
ostream& operator<<(ostream& stream, const my_FastCstrArray& array)
{
stream << "[ ";
for (int i = 0; i < array.length(); ++i) {
stream << '"' << array[i] << "\" ";
}
return stream << ']' << flush;
}