Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlma_autoreleaser
[Package bdlma]

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:
bdlma::AutoReleaser proctor to release memory to a managed allocator/pool
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:
  void release();
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 {
      // This class implements an array of C string elements.  Each C string
      // is allocated using the 'my_StrPool' member for fast memory
      // allocation and deallocation.

      // DATA
      char             **d_array_p;      // dynamically allocated array
      int                d_capacity;     // physical capacity of this array
      int                d_length;       // logical length of this array
      my_StrPool         d_strPool;      // memory manager to supply memory
      bslma::Allocator  *d_allocator_p;  // held, not owned

    private:
      // PRIVATE MANIPULATORS
      void increaseSize();

      // Not implemented:
      my_FastCstrArray(const my_FastCstrArray&);

    public:
      // CREATORS
      my_FastCstrArray(bslma::Allocator *basicAllocator = 0);
      ~my_FastCstrArray();

      // MANIPULATORS
      my_FastCstrArray& operator=(const my_FastCstrArray& rhs);
      void append(const char *item);

      // ACCESSORS
      const char *operator[](int index) const { return d_array_p[index]; }
      int length() const { return d_length; }
  };

  // FREE OPERATORS
  ostream& operator<<(ostream& stream, const my_FastCstrArray& array);
Then, we implement the methods:
  enum {
      k_MY_INITIAL_SIZE = 1, // initial physical capacity
      k_MY_GROW_FACTOR  = 2  // factor by which to grow 'd_capacity'
  };

  static inline
  int nextSize(int size)
      // Return the specified 'size' multiplied by 'k_MY_GROW_FACTOR'.
  {
      return size * k_MY_GROW_FACTOR;
  }

  static inline
  void reallocate(char             ***array,
                  int                *size,
                  int                 newSize,
                  int                 length,
                  bslma::Allocator   *allocator)
      // Reallocate memory in the specified 'array' and update the specified
      // 'size' to the specified 'newSize', using the specified 'allocator'
      // to supply memory.  The specified 'length' number of leading elements
      // are preserved.  If 'allocate' should throw an exception, this
      // function has no effect.  The behavior is undefined unless
      // '1 <= newSize', '0 <= length', and 'length <= newSize'.
  {
      ASSERT(array);
      ASSERT(*array);
      ASSERT(size);
      ASSERT(1 <= newSize);
      ASSERT(0 <= length);
      ASSERT(length <= *size);    // sanity check
      ASSERT(length <= newSize);  // ensure class invariant

      char **tmp = *array;

      *array = (char **)allocator->allocate(newSize * sizeof **array);

      // commit
      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);
  }

  // CREATORS
  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:
  // MANIPULATORS
  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;
  }

  // FREE OPERATORS
  ostream& operator<<(ostream& stream, const my_FastCstrArray& array)
  {
      stream << "[ ";
      for (int i = 0; i < array.length(); ++i) {
          stream << '"' << array[i] << "\" ";
      }
      return stream << ']' << flush;
  }