Provide a range proctor to managed a block of memory.
More...
Namespaces |
namespace | bslma |
Detailed Description
- Outline
-
-
- Purpose:
- Provide a range proctor to managed a block of memory.
-
- Classes:
-
- See also:
- Component bslma_deallocatorguard, Component bslma_deallocatorproctor
-
- Description:
- This component provides a range proctor class template,
bslma::AutoDeallocator
, to manage a sequence of blocks of (otherwise-unmanaged) memory of parameterized TYPE
supplied at construction. If not explicitly released, the sequence of managed memory blocks are deallocated automatically when the range proctor goes out of scope by freeing the memory using the parameterized ALLOCATOR
(allocator or pool) supplied at construction. Note that after a range proctor releases its managed sequence of memory, the same proctor can be reused to conditionally manage another sequence 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::AutoRawDeleter
class template 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::AutoDeallocator
proctor object can be used to achieve exception safety in an exception neutral way during manipulation of "out-of-place" arrays of raw resources or memory. Since there are no destructor calls, this component is more efficient compared to the bslma::AutoRawDeleter
. The following illustrates the insertion operation for an "out-of-place" array of raw character sequences. Assume that an array initially contains 5 character sequences as its elements: 0 1 2 3 4
_____ _____ _____ _____ _____
| o | o | o | o | o |
`==|==^==|==^==|==^==|==^==|=='
| _V___ | __V___ |
| |"Bye"| | |"berg"| |
| `=====' | `======' |
_V_____ _V_____ _V__
|"Hello"| |"Bloom"| |"LP"|
`=======' `=======' `===='
To insert two more character sequences at index position 2, the array is first reallocated if it is not big enough, and then the existing elements at index positions 2, 3, and 4 are shifted: 0 1 2 3 4 5 6
_____ _____ _____ _____ _____ _____ _____
| o | o |xxxxx|xxxxx| o | o | o |
`==|==^==|==^=====^=====^==|==^==|==^==|=='
| _V___ | __V___ |
| |"Bye"| | |"berg"| |
| `=====' | `======' |
_V_____ _V_____ _V__
|"Hello"| |"Bloom"| |"LP"|
`=======' `=======' `===='
Note: "xxxxx" denotes undefined value.
Next, two new memory blocks must be allocated to position 2 and 3. If, one of the two allocations fails and an exception is thrown, the array will be left in an invalid state because the addresses contained at index positions 2 and 3 may be duplicates of those at index positions 4 and 5, or, if a resize occurred, invalid altogether. We can restore exception neutrality by setting the array's length to 2 before attempting to create the string objects, but there is still a problem: the character sequences "Bloom", "berg", and "LP" (at index positions 4, 5, and 6) are "orphaned" and will never be deallocated -- a memory leak. To prevent this potential memory leak, we can additionally create a bslma::AutoDeallocator
object to manage (temporarily) the memory at index positions 4, 5, and 6 prior to allocating the new memory: 0 1 2 3 4 5 6
_____ _____ _____ _____ _____ _____ _____
| o | o |xxxxx|xxxxx| o | o | o |
`==|==^==|==^=====^=====^==|==^==|==^==|=='
| _V___ | __V___ |
| |"Bye"| | |"berg"| |
| `=====' | `======' |
_V_____ _V_____ _V__
|"Hello"| |"Bloom"| |"LP"|
`=======' `=======' `===='
my_StrArray ^---------------bslma::AutoDeallocator
(length = 2) (origin = 4, length = 3)
Note: Configuration after initializing the proctor.
If an exception occurs, the array (now of length 2) is in a perfectly valid state, while the proctor is responsible for deallocating the orphaned memory at index positions 4, 5, and 6. If no exception is thrown, the length is set to 7 and the proctor's release
method is called, releasing its control over the (temporarily) managed memory.
- The following example illustrates the use of
bslma::AutoDeallocator
to manage temporarily an "out-of-place" array of character sequences during the array's insertion operation.
- First we define a
my_StrArray
class that stores an array of character sequences.
template <class ALLOCATOR>
class my_StrArray {
char **d_array_p;
int d_length;
int d_size;
ALLOCATOR *d_allocator_p;
public:
my_StrArray(ALLOCATOR *basicAllocator);
~my_StrArray();
void append(const char *src);
void insert(int dstIndex, const my_StrArray& srcArray);
void insert2(int dstIndex, const my_StrArray& srcArray);
int length() const;
const char *operator[](int index) const;
};
Next, we define the insert
method of my_StrArray
: template <class ALLOCATOR>
void my_StrArray<ALLOCATOR>::insert(int dstIndex,
const my_StrArray<ALLOCATOR>& srcArray)
{
int srcLength = srcArray.d_length;
int newLength = d_length + srcLength;
int numShifted = d_length - dstIndex;
if (newLength > d_size) {
while (d_size < newLength) {
d_size = ! d_size ? 1 : 2 * d_size;
}
char ** newArray =
(char **) d_allocator_p->allocate(d_size * sizeof(char *));
memcpy(newArray, d_array_p, d_length * sizeof(char *));
if (d_array_p) {
d_allocator_p->deallocate(d_array_p);
}
d_array_p = newArray;
}
char **tmpSrc = srcArray.d_array_p;
if (this == &srcArray) {
size_t size = srcLength * sizeof(char *);
tmpSrc = (char **) d_allocator_p->allocate(size);
memcpy(tmpSrc, d_array_p, size);
}
bslma::DeallocatorProctor<ALLOCATOR> proctor(
this == &srcArray ? tmpSrc : 0,
d_allocator_p);
memmove(d_array_p + dstIndex + srcLength,
d_array_p + dstIndex,
numShifted * sizeof *d_array_p);
d_length = dstIndex;
bslma::AutoDeallocator<ALLOCATOR> tailDeallocator(
(void **) d_array_p + dstIndex + srcLength,
d_allocator_p,
numShifted);
Now, if any allocation for the inserted character sequences throws, the memory used for the character sequences that had been moved to the end of array will be deallocated automatically by the bslma::AutoDeallocator
.
for (int i = 0; i < srcLength; ++i, ++d_length) {
std::size_t size = std::strlen(tmpSrc[i]) + 1;
d_array_p[dstIndex + i] = (char *) d_allocator_p->allocate(size);
memcpy(d_array_p[dstIndex + i], tmpSrc[i], size);
}
tailDeallocator.release();
d_length = newLength;
}
The above method copies the source elements (visually) from left to right. Another (functionally equivalent) implementation copies the source elements from right to left, and makes use of the operator--()
of the bslma::AutoDeallocator
interface: template <class ALLOCATOR>
void my_StrArray<ALLOCATOR>::insert2(int dstIndex,
const my_StrArray<ALLOCATOR>& srcArray)
{
int srcLength = srcArray.d_length;
int newLength = d_length + srcLength;
int numShifted = d_length - dstIndex;
if (newLength > d_size) {
while (d_size < newLength) {
d_size = ! d_size ? 1 : 2 * d_size;
}
char ** newArray =
(char **) d_allocator_p->allocate(d_size * sizeof(char *));
memcpy(newArray, d_array_p, d_length * sizeof(char *));
if (d_array_p) {
d_allocator_p->deallocate(d_array_p);
}
d_array_p = newArray;
}
char **tmpSrc = srcArray.d_array_p;
if (this == &srcArray) {
size_t size = srcLength * sizeof(char *);
tmpSrc = (char **) d_allocator_p->allocate(size);
memcpy(tmpSrc, d_array_p, size);
}
bslma::DeallocatorProctor<ALLOCATOR> proctor(
this == &srcArray ? tmpSrc : 0,
d_allocator_p);
memmove(d_array_p + dstIndex + srcLength,
d_array_p + dstIndex,
numShifted * sizeof *d_array_p);
d_length = dstIndex;
bslma::AutoDeallocator<ALLOCATOR> tailDeallocator(
(void **) d_array_p + d_length + srcLength + numShifted,
d_allocator_p,
-numShifted);
Since we have decided to copy the source elements from right to left, we set the origin of the bslma::AutoDeallocator
to the end of the array, and decrement the (signed) length on each copy to extend the proctor range by 1.
for (int i = srcLength - 1; i >= 0; --i, --tailDeallocator) {
std::size_t size = std::strlen(tmpSrc[i]) + 1;
d_array_p[dstIndex + i] = (char *)d_allocator_p->allocate(size);
memcpy(d_array_p[dstIndex + i], tmpSrc[i], size);
}
tailDeallocator.release();
d_length = newLength;
}
Note that though the two implementations are functionally equivalent, they are logically different. First of all, the second implementation will be slightly slower because it is accessing memory backwards when compared to the normal forward sequential access. Secondly, in case of an exception, the first implementation will retain all the elements copied prior to the exception, whereas the second implementation will remove them.