Provide a proxy for constructing and destroying objects.
More...
Detailed Description
- Outline
-
-
- Purpose:
- Provide a proxy for constructing and destroying objects.
-
- Classes:
-
- See also:
- Component bslma_allocator
-
- Description:
- This component provides a proxy for constructing and automatically destroying objects. The proxy class
bslalg::ConstructorProxy
is parameterized on a OBJECT_TYPE
, where OBJECT_TYPE
may or may not use a bslma
allocator to supply memory. Upon construction of a proxy, a proxied OBJECT_TYPE
instance is also constructed; the bslma
allocator supplied to the proxy constructor is passed to the constructor of the proxied object only if OBJECT_TYPE
declares the bslma::UsesBslmaAllocator
trait. If this trait is not declared for OBJECT_TYPE
, the allocator is ignored.
- Following construction of the proxied object, it is held by the proxy. Modifiable and non-modifiable access to the proxied object may be obtained using the overloaded
object
methods. When the proxy is destroyed, the proxied object is automatically destroyed.
- This proxy is useful in situations where an object of a given type must be constructed, but it is not known whether the object's constructor takes a
bslma
allocator to supply memory. This occurs frequently in generic programming.
- See the
bslma
package-level documentation for more information about using bslma
allocators.
-
- Usage:
- The snippets of code in the first usage example below illustrate very basic use of the constructor proxy. The second usage example provides a more extended illustration of a scenario that can occur in generic programming.
-
- Example 1:
- Suppose we have an arbitrary class:
SomeClass
may optionally declare the bslma::UsesBslmaAllocator
trait. The following code illustrates how a SomeClass
object can be constructed using a constructor proxy that detects this trait: If SomeClass
declares the bslma::UsesBslmaAllocator
trait, then the object of type SomeClass
held by proxy
will obtain its memory from the supplied testAllocator
. Otherwise, testAllocator
will be ignored.
-
- Example 2:
- The following snippets of code illustrate a use of this component in a more typical scenario.
- The
MyContainer
class below contains an object of the specified parameter TYPE
: template <typename TYPE>
class MyContainer {
TYPE d_object;
public:
explicit MyContainer(bslma::Allocator *basicAllocator = 0);
~MyContainer();
const TYPE& getObject() const;
};
The implementation for the MyContainer
constructor is a little tricky without a constructor proxy. One possible implementation is as follows: template <typename TYPE>
MyContainer<TYPE>::MyContainer(bslma::Allocator *basicAllocator)
{
}
This implementation will compile successfully for each TYPE
that has a default constructor, but it will not behave as documented. In particular, the specified basicAllocator
will not be used to supply memory.
- Another possible implementation for the
MyContainer
constructor is as follows: template <typename TYPE>
MyContainer<TYPE>::MyContainer(bslma::Allocator *basicAllocator)
: d_object(basicAllocator)
{
}
This implementation behaves as documented, but it will not compile unless TYPE
has a constructor taking a bslma::Allocator *
. For example, the following declaration of container
will fail to compile: The solution to this problem is to use the constructor proxy provided by this component. The following definition of MyContainer
uses a constructor proxy for the contained object of parameterized TYPE
: The constructor for MyContainer
can now be implemented as follows: template <typename TYPE>
MyContainer<TYPE>::MyContainer(bslma::Allocator *basicAllocator)
: d_proxy(basicAllocator)
{
}
The getObject
method of MyContainer
is implemented as follows: template <typename TYPE>
const TYPE& MyContainer<TYPE>::getObject() const
{
return d_proxy.object();
}
Now the following code, which previously did not compile, will compile successfully: The specified testAllocator
will simply be ignored because int
does not use a bslma
allocator to supply memory.
- Next suppose we have a class defined as follows:
class SomeClassUsingAllocator {
bslma::Allocator *d_allocator_p;
public:
explicit SomeClassUsingAllocator(bslma::Allocator *basicAllocator = 0)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
{
}
bslma::Allocator *getAllocator() const
{
return d_allocator_p;
}
};
namespace bslma {
template <>
struct UsesBslmaAllocator<SomeClassUsingAllocator> : bsl::true_type
{};
}
The following code will compile and run without an assertion failure: bslma::TestAllocator testAllocator;
MyContainer<SomeClassUsingAllocator> container(&testAllocator);
assert(&testAllocator == container.getObject().getAllocator());
Finally, since the MyContainer
class uses a bslma
allocator to supply memory, it is useful to expose this property. This is done by declaring the bslma::UsesBslmaAllocator
trait to complete the definition of MyContainer
: template <typename TYPE>
class MyContainer {
bslalg::ConstructorProxy<TYPE> d_proxy;
public:
explicit MyContainer(bslma::Allocator *basicAllocator = 0);
~MyContainer();
const TYPE& getObject() const;
};
namespace bslma {
template <typename TYPE>
struct UsesBslmaAllocator<MyContainer<TYPE> > : bsl::true_type
{};
}
The following code will also compile and run without an assertion failure: bslma::TestAllocator testAllocator;
MyContainer<MyContainer<SomeClassUsingAllocator> >
containedContainer(&testAllocator);
assert(&testAllocator
== containedContainer.getObject().getObject().getAllocator());