BDE 4.14.0 Production release
|
Provide a pure abstract interface for memory-allocation mechanisms.
Canonical header: bsl_memory_resource.h
This component is for internal use only. Please include <bsl_memory_resource.h>
instead and use bsl::memory_resource
directly.
This component provides a protocol (pure abstract interface) class, bsl::memory_resource
, comprising member functions for allocating and deallocating memory. The bsl::memory_resource
interface is identical to that of std::pmr::memory_resource
from the C++17 Standard Library; in fact, the former type is an alias for the latter type when using a C++17 or later library supplied by the platform.
A concrete class derived from bsl::memory_resource
might use pooling or other mechanisms that improve on new
and delete
in some way, such as speeding up the program or providing instrumentation for debugging or security. A memory_resource
thus provides a customizable alterantive to using raw calls to new
and delete
.
An object, Obj
, holding a base-class pointer, d_resource_p
, of type bsl::memory_resource *
would allocate and deallocate memory by calling the resource's allocate
and deallocate
member functions (through d_resource_p
), which subsequently invoke the respective virtual functions, do_allocate
and do_deallocate
. A client can thus customize the memory allocation mechanism used by Obj
by providing it with an appropriate concrete resource whose class overrides do_allocate
and do_deallocate
.
Unless otherwise documented, a single memory resource object is not safe for concurrent access by multiple threads. Classes derived from bsl::memory_resource
that are specifically designed for concurrent access must be documented as such. Unless specifically documented otherwise, separate objects of classes derived from bsl::memory_resource
may safely be used in separate threads.
Note that some memory resources delegate to other memory resource objects. When used in a concurrent context, the thread safety of the entire chain must be considered.
The bsl::memory_resource
protocol provided in this component defines a bilateral contract between suppliers and consumers of raw memory. The following subsections illustrate (1) implementation of a concrete resource derived from the abstract bsl::memory_resource
base class and (2) use of a bsl::memory_resource
.
In this example, we derive a concrete CountingResource
class from bsl::memory_resource
, overriding and providing concrete implementations for all of the virtual functions declared in the base class. This resource keeps track of the number of blocks of memory that were allocated from the resource but not yet returned to the resource.
First, we define the CountingResource
class with a single private data member to keep track of the number of blocks outstanding. We don't want this type to be copyable, so we disable copying here, too.
Next, we declare the protected virtual functions that override the base-class virtual functions:
Now we can declare the public interface, comprising the default constructor, the destructor, and an accessor to return the current block count; all other public members are inherited from the base class:
Next, we implement the do_allocate
method to allocate memory using operator new
, then increment the block counter. We cannot, in C++11, force operator new
to return memory that is more than maximally aligned, so we throw an exception if the specified alignment
is not met; other resources can use the alignment
argument more productively.
Next, we implement do_deallocate
, which returns the memory referenced by p
to the heap and decrements the block counter. The bytes
and alignment
arguments are ignored:
Next, we implement do_is_equal
, which determines if the specified other
resource is equal to this one. For this and most other resource types, do_is_equal
returns true
if and only if the two resources are the same object:
Next, we implement the destructor, which simply asserts that the block count is zero upon destruction:
Finally, we construct an object of CountingResource
and verify that allocation, deallocation, and equality testing work as expected.
In this example, we define a class template, Holder<TYPE>
, that holds a single instance of TYPE
on the heap. Holder
is designed such that its memory use can be customized by supplying an appropriate memory resource. A holder object can be empty and it can be move-constructed even if TYPE
is not movable. In addition, the footprint of a Holder
object is the same (typically the size of 2 pointers), regardless of the size of TYPE
.
First, we define a simple class template modeled after the C++17 standard library std::pmr::polymorphic_allocator
template, which is a thin wrapper around a memory_resource
pointer. By wrapping the pointer in a class, we avoid some the problems of raw pointers such as accidental use of a null pointer:
Next, we implement the constructor for PolymorphicAllocator
, which stores the pointer argument and defensively checks that it is not null:
Next, we implement the allocation and deallocation functions by forwarding to the corresponding function of the memory resource. Note that the size and alignment of TYPE
are used to compute the appropriate number of bytes and alignment to request from the memory resource:
Now we define our actual Holder
template with with data members to hold the memory allocator and a pointer to the contained object:
Next, we declare the constructors. Following the pattern for allocator-aware types used in BDE, the public interface contains an allocator_type
typedef that can be passed to each constructor. Typically, the allocator constructor argument would be optional, but, because our PolymorphicAllocator
has no default constructor (unlike the std::pmr::polymorphic_allocator
), the allocator is required for all constructors except the move constructor:
Next, we declare the manipulators and accessors, allowing a Holder
to be assigned and giving a client access to its value and allocator:
Next, we'll implement the first constructor, which creates an empty object; its only job is to store the allocator:
Next, we'll implement the second constructor, which allocates memory and constructs an object in it. The try
/catch
block is needed to free the memory in case the constructor for TYPE
throws and exception. An alternative implementation would use an RAII object to automatically free the memory in the case of an exception (see bslma_deallocatorproctor ):
Next, we'll implement a destructor that deletes the value object and deallocates the allocated memory:
Finally, we've implemented enough of Holder
to demonstrate its use. Below, we pass the CountingResource
from Example 1 to the constructors several Holder
objects. Each non-empty Holder
allocates one block of memory, which is reflected in the outstanding block count. Note that the address of the resource can be passed directly to the constructors because PolymorphicAllocator
is implicitly convertible from 'bsl::memory_resource *':