BDE 4.14.0 Production release
|
Typedefs | |
typedef bslma::Default | bdema_Default |
typedef bslma::Default | bslma_Default |
This alias is defined for backward compatibility. | |
Provide utilities to set/fetch the default and global allocators.
This component provides a set of utility functions that manage the addresses of two distinguished memory allocators: the default allocator and the global allocator. Each of these allocators are of type derived from bslma::Allocator
. Note that for brevity in the following we will generally refer to "the address of the default allocator" as simply "the default allocator" (and similarly for the global allocator).
The global allocator is intended to be used as the allocator for (global) singleton objects. In general, the default allocator is for all other memory allocations in contexts where an alternative allocator is not explicitly specified (or cannot be specified as, for example, when a compiler-generated temporary object of a type that requires an allocator is created).
Initially, both the default allocator and global allocator resolve to the address of the bslma::NewDeleteAllocator
singleton, i.e.:
Methods are provided to retrieve and set the two allocators independently. The following two subsections supply further details, in turn, on the methods that pertain to the default and global allocators.
Two methods provide access to the default allocator, bslma::Default::defaultAllocator
and bslma::Default::allocator
(the latter when called with no argument, or an explicit 0). When bslma::Default::allocator
is supplied with a non-0 argument, it simply returns that argument to the caller (i.e., it acts as a pass-through). A (non-singleton) class that is designed to take advantage of an allocator will typically revert to the default allocator whenever a constructor is called without an allocator (yielding the default argument value of 0). The bslma::Default::allocator
method facilitates this behavior. See the usage examples below for an illustration of this technique.
The default allocator can be set prior to a call to bslma::Default::defaultAllocator
, to bslma::Default::allocator
with no argument or an explicit 0, or to bslma::Default::lockDefaultAllocator
, by calling bslma::Default::setDefaultAllocator
. This method returns 0 on success and a non-zero value on failure. This method fails if the default allocator is "locked". The default allocator is initially unlocked. It is explicitly locked by calling bslma::Default::lockDefaultAllocator
. In addition, the default allocator is implicitly locked as a side-effect of calling bslma::Default::defaultAllocator
, or bslma::Default::allocator
with no argument or an explicit 0. Once locked, the default allocator cannot be unlocked. However, the bslma::Default::setDefaultAllocatorRaw
method will unconditionally set the default allocator regardless of whether it is locked. When the C++17 pmr
library is available, setting the default allocator by any of these methods also sets the default memory resource to the same value by invoking std::pmr::set_default_resource
.
A well-behaved program should call bslma::Default::setDefaultAllocator
once. It should be invoked in main
before starting any threads, and be followed immediately by a call to 'bslma::Default::lockDefaultAllocator. Note that bslma::Default::setDefaultAllocatorRaw
is provided for testing only, and should typically never be used in a production environment.
When using a platform library that supports the C++17 PMR interface, a successful change to the default allocator will also result in changing the default memory resource returned by std::pmr::get_default_resource
to the same value. This automatic syncronization can be broken by deliberately calling std::pmr::set_default_resource
, which will change the default memory resource without also changing the default allocator. Note that std::pmr::get_default_resource
does not lock the default memory resource the way bslma::Default::defaultAllocator
locks the default allocator.
WARNING: Note that the default allocator can become locked prior to entering main
as a side-effect of initializing a file-scope static object. For example, the presence of a global bsl::string
object in an executable will have this unintended consequence. Further note that this phenomenon can vary across platforms. In particular, linkers differ as to the aggressiveness with which they pull in file-scope static objects from the libraries that are on the link line. AVOID file-scope static objects that require runtime initialization, especially those that take an allocator.
The interface pertaining to the global allocator is comparatively much simpler, consisting of just two methods. The bslma::Default::globalAllocator
method, when called with no argument (or an explicit 0), returns the global allocator currently in effect at the point of call. It has no side-effects. When supplied with a non-0 argument, bslma::Default::globalAllocator
simply returns that argument to the caller (i.e., it acts as a pass-through similar to bslma::Default::allocator
when it is supplied with a non-0 argument). The global allocator may be set using the bslma::Default::setGlobalAllocator
method. This method always succeeds. In that respect, the global allocator cannot become locked like the default allocator. bslma::Default::setGlobalAllocator
returns the global allocator that is in effect upon entry to the function.
Note that bslma::Default::setGlobalAllocator
should be used with extreme caution. In particular, a well-behaved program should call this function at most once. If called, it should be invoked in main
before starting any threads and before initializing singletons.
The following sequence of usage examples illustrate recommended use of the default and global allocators. The examples employ the following simple memory allocator, my_CountingAllocator
, that counts both the number of memory blocks that have been allocated, but not yet deallocated, and the cumulative number of blocks ever allocated. The two values are available through the accessors numBlocksInUse
and numBlocksTotal
, respectively. For actual allocations and deallocations, my_CountingAllocator
uses global operators new
and delete
:
The virtual
methods of my_CountingAllocator
are defined in the component .cpp
file:
This usage example illustrates the basics of class design that relate to proper use of the default allocator, and introduces the standard pattern to apply when setting (and locking) the default allocator. First we define a trivial class, my_Id
, that uses an allocator. my_Id
simply encapsulates a C-style (null-terminated) id string that is accessible through the id
method. Note that each constructor is declared to take an optional bslma::Allocator *
as its last argument. Also note that the expression:
is used in applicable member initializers to propagate each constructor's allocator argument to the data members that require it (in this case, the object allocator that is held by each my_Id
object). If basicAllocator
is 0, the object is created using the default allocator. Otherwise, the explicitly supplied allocator is used:
Next we set the default allocator to one of our counting allocator objects. Note that immediately after successfully setting it, we lock the default allocator, so that subsequent calls to bslma::Default::setDefaultAllocator
fail. (The default allocator can still be modified by calling bslma::Default::setDefaultAllocatorRaw
, but calling that function in production code is anti-social. Our usage examples expressly do not call that method.) With the possible exception of test drivers, the default allocator should be set and locked early in main
before threads are started and before objects are initialized:
In the following, we instantiate two objects of type my_Id
. The first object, idA
, is not supplied with an allocator, so it uses the default allocator. The second object, idB
, is supplied with an object of type my_CountingAllocator
. The assertions track the states of the two allocators at each point in the code fragment. In particular, note that the state of the default allocator does not change during the lifetime of idB
:
This example demonstrates how the default allocator is used to detect a very common programming error pertaining to allocator usage. First we define the trivial (but buggy) my_IdPair
class:
The definition of the my_IdPair
constructor above intentionally includes a common programming error: The allocator in use by the object is not passed to all data members that require it. We will see shortly how this error is detected at runtime using the default allocator.
Next, the default allocator is set and locked identically to what was done in usage example 1:
Now we instantiate an object of type my_IdPair
without explicitly specifying an allocator. As a result, the object uses the default allocator. The assertions verify the expected changes in the state of the default allocator:
Next we instantiate a second object of type my_IdPair
, this time supplying it with a counting allocator object that is distinct from the default allocator. The assertions in the following code fragment that are commented out indicate the expected states of the allocators (i.e., in a bug-free implementation of my_IdPair
) after the object has been constructed and again after it has been destroyed. However, due to the (intentional) bug in the constructor, the uncommented assertions reveal the true state of affairs:
Note that, although not necessary in the case of the simple my_IdPair
class, the default allocator can be used (and typically should be used) within the body of a constructor, or any other member function, to allocate dynamic memory that is temporarily needed by the method (and, hence, not owned by the object after the method has returned). Thus, the invariant that must hold immediately after a method of an object returns is that the value returned by defaultCountingAllocator.numBlocksInUse()
must be identical to what it was immediately prior to calling the method. Of course, note that the above invariant pertains to cases in single-threaded programs where the object allocator in use by the object is distinct from the default allocator. Also note that the value returned by defaultCountingAllocator.numBlocksTotal()
can differ across function invocations (i.e., even in correct code).
Next we define a simple singleton class, my_Singleton
, that defaults to using the global allocator if one is not explicitly specified when the singleton object is initialized. Toward that end, note that in contrast to my_Id
, the constructor for my_Singleton
uses:
in its member initializer:
The following completes the definition of my_Singleton
in the component .cpp
file:
In the following, the default and global allocators are set to distinct instances of my_CountingAllocator
. Note that the default allocator is set and locked identically to what was done in the previous two usage examples:
Finally, we initialize the singleton object. We explicitly specify the desired allocator in the call to initSingleton
to make our intentions as clear as possible. Of course, because of the way the my_Singleton
constructor was written, the result would have been the same if no allocator had been specified. As in previous examples, the states of the default and global allocators are asserted before and after initializing the singleton:
typedef bslma::Default bdema_Default |
This struct
is a namespace for functions that manipulate and access the default and global allocator pointers. This alias is defined for backward compatibility.
typedef bslma::Default bslma_Default |