Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslma_managedptr
[Package bslma]

Provide a managed pointer class. More...

Namespaces

namespace  bslma

Detailed Description

Outline
Purpose:
Provide a managed pointer class.
Classes:
bslma::ManagedPtr proctor for automatic memory management
bslma::ManagedPtrUtil namespace for ManagedPtr-related utility functions
See also:
bslmf_ispolymporphic
Description:
This component provides a proctor, bslma::ManagedPtr, similar to bsl::auto_ptr, that supports user-specified deleters. The proctor is responsible for the automatic destruction of the object referenced by the managed pointer. As a "smart pointer", this object offers an interface similar to a native pointer, supporting dereference operators (*, ->), (in)equality comparison and testing as if it were a boolean value. However, like bsl::auto_ptr it has unusual "copy-semantics" that transfer ownership of the managed object, rather than making a copy. It should be noted that this signature does not satisfy the requirements for an element-type stored in any of the standard library containers. Note that this component will fail to compile when instantiated for a class that gives a false-positive for the type trait bslmf::IsPolymorphic. See the bslmf_ispolymporphic component for more details.
This component also provides the bslma::ManagedPtrUtil struct, which defines a namespace for utility functions that facilitate working with ManagedPtr objects. Of particular note are the allocateManaged and makeManaged class methods that can be used to create a managed object as well as a ManagedPtr to manage it, with the latter being returned. allocateManaged takes a bslma::Allocator * argument that is both (1) used to allocate the footprint of the managed object and (2) used by the managed object itself if it defines the bslma::UsesBslmaAllocator trait. makeManaged does not take a bslma::Allocator * argument and uses the default allocator to allocate the footprint of the managed object instead.
Factories:
An object that will be managed by a ManagedPtr object is typically dynamically allocated and destroyed by a factory. For the purposes of this, component, a factory is any class that provides a deleteObject function taking a single argument of the (pointer) type of the managed pointer. The following is an example of a factory deleter:
  class my_Factory {

     // . . .

     // MANIPULATORS
     my_Type *createObject(bslma::Allocator *basicAllocator = 0);
         // Create a 'my_Type' object.  Optionally specify a
         // 'basicAllocator' used to supply memory.  If 'basicAllocator' is
         // 0, the currently installed default allocator is used.

     void deleteObject(my_Type *object);
         // Delete the specified 'object'.
  };

  class my_Allocator : public bslma::Allocator { /* ... */ };
Note that deleteObject is provided by all bslma allocators and by any object that implements the bdlma::Deleter protocol. Thus, any of these objects can be used as a factory deleter. The purpose of this design is to allow bslma allocators and factories to be used seamlessly as deleters.
Note that when the ManagedPtr(MANAGED_TYPE *) constructor is used, the managed object will be destroyed with a built-in deleter that calls delete ptr, but when the ManagedPtr(MANAGED_TYPE *, FACTORY_TYPE *) constructor is called with 0 == factory, the currently installed default allocator will be used as the factory.
Deleters:
When a managed pointer is destroyed, the managed object is destroyed using the user supplied "deleter". A deleter is simply a function that is invoked with two void * arguments: a pointer to the object to be destroyed, and a pointer to a cookie that is supplied at the same time as the deleter and managed object.
  typedef void (*DeleterFunc)(void *managedObject, void *cookie);
The meaning of the cookie depends on the specific deleter. Typically a deleter function will accept the two void * pointers and internally cast them to the appropriate types for pointers to the managed object and cookie. Note that there are no methods taking just a deleter, as the user must always supply a cookie to be passed when the deleter is actually invoked.
Note that this component still supports (deprecated) legacy deleters that expect to be passed pointers to the specific cookie and managed object types in use. This latter form of deleter was deprecated as it relies on undefined behavior, casting such function pointers to the correct form (taking two void * arguments) and invoking the function with two void * pointer arguments. While this is undefined behavior, it is known to have the desired effect on all platforms currently in use.
Aliasing:
In a managed pointer, the pointer value (the value returned by the get method) and the pointer to the managed object need not have the same value. The loadAlias method allows a managed pointer to be created as an "alias" to another managed pointer (possibly of a different type), which we'll call the "original" managed pointer. When get is invoked on the alias, the aliased pointer value is returned, but when the managed pointer is destroyed, the original managed object will be passed to the deleter. (See also the documentation of the alias constructor or of the loadAlias method.)
Exception Safety:
The principal usage of a managed pointer is to guarantee that a local object will be deallocated properly should an operation throw after its allocation. In this, it is very similar to bsl::auto_ptr. It is required for the proper functioning of this component that a deleter does not throw at invocation (upon destruction or re-assignment of the managed pointer).
Type Casting:
ManagedPtr objects can be implicitly and explicitly cast to different types in the same way that native pointers can.
Explicit Casting:
Through "aliasing", a managed pointer of any type can be explicitly cast to a managed pointer of any other type using any legal cast expression. See example 4 on type casting below for more details.
Implicit Casting:
As with native pointers, a managed pointer of the type B that is derived from the type A, can be directly assigned to a ManagedPtr of A. Likewise a managed pointer of type B can be directly assigned to a ManagedPtr of const B. However, the rules for construction are a little more subtle, and apply when passing a bslma::ManagedPtr by value into a function, or returning as the result of a function.
  class A {};

  class B : public A {};

  void test()
  {
      B *b_p = 0;
      A *a_p = b_p;

      bslma::ManagedPtr<B> b_mp1;
      bslma::ManagedPtr<A> a_mp1(b_mp1);   // direct-initialization is valid
      bslma::ManagedPtr<A> a_mp2 = b_mp1;  // copy-initialization should fail
  }
Note that std::auto_ptr has the same restriction, and this failure will occur only on compilers that strictly conform to the C++ standard, such as recent gcc compilers or (in this case) IBM xlC.
Usage:
In this section we show intended usage of this component.
Example 1: Implementing a Protocol:
We demonstrate using ManagedPtr to configure and return a managed object implementing an abstract protocol.
First we define our protocol, Shape, a type of object that knows how to compute its area. Note that for expository reasons only, we do not give Shape a virtual destructor.
  struct Shape {
      virtual double area() const = 0;
          // Return the 'area' of this shape.
  };
Then we define a couple of classes that implement the Shape protocol, a Circle and a Square.
  class Circle : public Shape {
    private:
      // DATA
      double d_radius;

    public:
      // CREATORS
      explicit Circle(double radius);
          // Create a 'Circle' object having the specified 'radius'.

      // ACCESSORS
      virtual double area() const;
          // Return the area of this Circle, given by the formula pi*r*r.
  };

  class Square : public Shape {
    private:
      // DATA
      double d_sideLength;

    public:
      // CREATORS
      explicit Square(double side);
          // Create a 'Square' having sides of length 'side'.

      // ACCESSORS
      virtual double area() const;
          // Return the area of this Square, given by the formula side*side
  };
Next we implement the methods for Circle and Square.
  Circle::Circle(double radius)
  : d_radius(radius)
  {
  }

  double Circle::area() const {
      return 3.141592653589793238462 * d_radius * d_radius;
  }

  Square::Square(double side)
  : d_sideLength(side)
  {
  }

  double Square::area() const {
      return d_sideLength * d_sideLength;
  }
Then we define an enumeration that lists each implementation of the Shape protocol.
  struct Shapes {
      enum VALUES { SHAPE_CIRCLE, SHAPE_SQUARE };
  };
Now we can define a function that will return a Circle object or a Square object according to the specified kind parameter, and having its dimension specified by the caller.
  bslma::ManagedPtr<Shape> makeShape(Shapes::VALUES kind, double dimension)
  {
      bslma::Allocator *alloc = bslma::Default::defaultAllocator();
      bslma::ManagedPtr<Shape> result;
      switch (kind) {
        case Shapes::SHAPE_CIRCLE: {
          Circle *circ = new(*alloc)Circle(dimension);
          result.load(circ);
        } break;
        case Shapes::SHAPE_SQUARE: {
          Square *sqr = new(*alloc)Square(dimension);
          result.load(sqr);
        } break;
      }
      return result;
  }
Then, we can use our function to create shapes of different kinds, and check that they report the correct area. Note that we are using a radius of 1.0 for the Circle and integral side-length for the Square to support an accurate operator== with floating-point quantities. Also note that, despite the destructor for Shape being non-virtual, the correct destructor for the appropriate concrete Shape type is called. This is because the destructor is captured when the ManagedPtr constructor is called, and has access to the complete type of each shape object.
  void testShapes()
  {
      bslma::ManagedPtr<Shape> shape = makeShape(Shapes::SHAPE_CIRCLE, 1.0);
      assert(0 != shape);
      assert(3.141592653589793238462 == shape->area());

      shape = makeShape(Shapes::SHAPE_SQUARE, 2.0);
      assert(0 != shape);
      assert(4.0 == shape->area());
  }
Next, we observe that as we are creating objects dynamically, we should pass an allocator to the makeShape function, rather than simply accepting the default allocator each time. Note that when we do this, we pass the user's allocator to the ManagedPtr object as the "factory".
  bslma::ManagedPtr<Shape> makeShape(Shapes::VALUES    kind,
                                     double            dimension,
                                     bslma::Allocator *allocator)
  {
      bslma::Allocator *alloc = bslma::Default::allocator(allocator);
      bslma::ManagedPtr<Shape> result;
      switch (kind) {
        case Shapes::SHAPE_CIRCLE: {
          Circle *circ = new(*alloc)Circle(dimension);
          result.load(circ, alloc);
        } break;
        case Shapes::SHAPE_SQUARE: {
          Square *sqr = new(*alloc)Square(dimension);
          result.load(sqr, alloc);
        } break;
      }
      return result;
  }
Finally we repeat the earlier test, additionally passing a test allocator:
  void testShapesToo()
  {
      bslma::TestAllocator ta("object");

      bslma::ManagedPtr<Shape> shape =
                                   makeShape(Shapes::SHAPE_CIRCLE, 1.0, &ta);
      assert(0 != shape);
      assert(3.141592653589793238462 == shape->area());

      shape = makeShape(Shapes::SHAPE_SQUARE, 3.0, &ta);
      assert(0 != shape);
      assert(9.0 == shape->area());
  }
Example 2: Aliasing:
Suppose that we wish to give access to an item in a temporary array via a pointer, which we will call the "finger". The finger is the only pointer to the array or any part of the array, but the entire array must be valid until the finger is destroyed, at which time the entire array must be deleted. We handle this situation by first creating a managed pointer to the entire array, then creating an alias of that pointer for the finger. The finger takes ownership of the array instance, and when the finger is destroyed, it is the array's address, rather than the finger, that is passed to the deleter.
First, let's say our array stores data acquired from a ticker plant accessible by a global getQuote function:
  struct Ticker {

      static double getQuote() // From ticker plant.  Simulated here
      {
          static const double QUOTES[] = {
          7.25, 12.25, 11.40, 12.00, 15.50, 16.25, 18.75, 20.25, 19.25, 21.00
          };
          static const int NUM_QUOTES = sizeof(QUOTES) / sizeof(QUOTES[0]);
          static int index = 0;

          double ret = QUOTES[index];
          index = (index + 1) % NUM_QUOTES;
          return ret;
      }
  };
Then, we want to find the first quote larger than a specified threshold, but would also like to keep the earlier and later quotes for possible examination. Our getFirstQuoteLargerThan function must allocate memory for an array of quotes (the threshold and its neighbors). It thus returns a managed pointer to the desired value:
  const double END_QUOTE = -1;

  bslma::ManagedPtr<double>
  getFirstQuoteLargerThan(double threshold, bslma::Allocator *allocator)
  {
      assert(END_QUOTE < 0 && 0 <= threshold);
Next, we allocate our array with extra room to mark the beginning and end with a special END_QUOTE value:
      const int MAX_QUOTES = 100;
      int numBytes = (MAX_QUOTES + 2) * sizeof(double);
      double *quotes = (double *) allocator->allocate(numBytes);
      quotes[0] = quotes[MAX_QUOTES + 1] = END_QUOTE;
Then, we create a managed pointer to the entire array:
      bslma::ManagedPtr<double> managedQuotes(quotes, allocator);
Next, we read quotes until the array is full, keeping track of the first quote that exceeds the threshold.
      double *finger = 0;

      for (int i = 1; i <= MAX_QUOTES; ++i) {
          double quote = Ticker::getQuote();
          quotes[i] = quote;
          if (!finger && quote > threshold) {
              finger = &quotes[i];
          }
      }
Now, we use the alias constructor to create a managed pointer that points to the desired value (the finger) but manages the entire array:
      return bslma::ManagedPtr<double>(managedQuotes, finger);
  }
Then, our main program calls getFirstQuoteLargerThan like this:
  int aliasExample()
  {
      bslma::TestAllocator ta;
      bslma::ManagedPtr<double> result = getFirstQuoteLargerThan(16.00, &ta);
      assert(*result > 16.00);
      assert(1 == ta.numBlocksInUse());
      if (g_verbose) bsl::cout << "Found quote: " << *result << bsl::endl;
Next, we also print the preceding 5 quotes in last-to-first order:
      if (g_verbose) bsl::cout << "Preceded by:";
      int i;
      for (i = -1; i >= -5; --i) {
          double quote = result.get()[i];
          if (END_QUOTE == quote) {
              break;
          }
          assert(quote < *result);
          if (g_verbose) bsl::cout << ' ' << quote;
      }
      if (g_verbose) bsl::cout << bsl::endl;
Then, to move the finger, e.g., to the last position printed, one must be careful to retain the ownership of the entire array. Using the statement result.load(result.get()-i) would be an error, because it would first compute the pointer value result.get()-i of the argument, then release the entire array before starting to manage what has now become an invalid pointer. Instead, result must retain its ownership to the entire array, which can be attained by:
      result.loadAlias(result, result.get()-i);
Finally, if we reset the result pointer, the entire array is deallocated:
      result.reset();
      assert(0 == ta.numBlocksInUse());
      assert(0 == ta.numBytesInUse());

      return 0;
  }
Example 3: Dynamic Objects and Factories:
Suppose we want to track the number of objects currently managed by ManagedPtr objects.
First we define a factory type that holds an allocator and a usage-counter. Note that such a type cannot sensibly be copied, as the notion count becomes confused.
  class CountedFactory {
      // DATA
      int               d_count;
      bslma::Allocator *d_allocator_p;

      // NOT IMPLEMENTED
      CountedFactory(const CountedFactory&);
      CountedFactory& operator=(const CountedFactory&);

    public:
      // CREATORS
      explicit CountedFactory(bslma::Allocator *alloc = 0);
          // Create a 'CountedFactory' object which uses the supplied
          // allocator 'alloc'.

      ~CountedFactory();
          // Destroy this object.
Next, we provide the createObject and deleteObject functions that are standard for factory objects. Note that the deleteObject function signature has the form required by bslma::ManagedPtr for a factory.
      // MANIPULATORS
      template <class TYPE>
      TYPE *createObject();
          // Return a pointer to a newly allocated object of type 'TYPE'
          // created using its default constructor.  Memory for the object
          // is supplied by the allocator supplied to this factory's
          // constructor, and the count of valid object is incremented.

      template <class TYPE>
      void deleteObject(const TYPE *target);
          // Destroy the object pointed to by 'target' and reclaim the
          // memory.  Decrement the count of currently valid objects.
Then, we round out the class with the ability to query the count of currently allocated objects.
      // ACCESSORS
      int count() const;
          // Return the number of currently valid objects allocated by this
          // factory.
  };
Next, we define the operations declared by the class.
  CountedFactory::CountedFactory(bslma::Allocator *alloc)
  : d_count(0)
  , d_allocator_p(bslma::Default::allocator(alloc))
  {
  }

  CountedFactory::~CountedFactory()
  {
      assert(0 == d_count);
  }

  template <class TYPE>
  TYPE *CountedFactory::createObject()
  {
      TYPE *result = new(*d_allocator_p)TYPE;
      ++d_count;
      return result;
  }

  template <class TYPE>
  void CountedFactory::deleteObject(const TYPE *object)
  {
      d_allocator_p->deleteObject(object);
      --d_count;
  }

  inline
  int CountedFactory::count() const
  {
      return d_count;
  }
Then, we can create a test function to illustrate how such a factory would be used with ManagedPtr.
  void testCountedFactory()
  {
Next, we declare a test allocator, and an object of our CountedFactory type using that allocator.
      bslma::TestAllocator ta;
      CountedFactory cf(&ta);
Then, we open a new local scope and declare an array of managed pointers. We need a local scope in order to observe the behavior of the destructors at end of the scope, and use an array as an easy way to count more than one object. Next, we load each managed pointer in the array with a new int using our factory cf and assert that the factory count is correct after each new int is created.
          int i = 0;
          while (i != 4) {
              pData[i++].load(cf.createObject<int>(), &cf);
              assert(cf.count() == i);
          }
Then, we reset the contents of a single managed pointer in the array, and assert that the factory count is appropriately reduced.
          pData[1].reset();
          assert(3 == cf.count());
Next, we load a managed pointer with another new int value, again using cf as the factory, and assert that the count of valid objects remains the same (destroy one object and add another).
          pData[2].load(cf.createObject<int>(), &cf);
          assert(3 == cf.count());
      }
Finally, we allow the array of managed pointers to go out of scope and confirm that when all managed objects are destroyed, the factory count falls to zero, and does not overshoot.
      assert(0 == cf.count());
  }
Example 4: Type Casting:
ManagedPtr objects can be implicitly and explicitly cast to different types in the same way that native pointers can.
Implicit Conversion:
As with native pointers, a pointer of the type B that is publicly derived from the type A, can be directly assigned a ManagedPtr of A.
First, consider the following code snippets:
  void implicitCastingExample()
  {
If the statements:
      bslma::TestAllocator localDefaultTa;
      bslma::TestAllocator localTa;

      bslma::DefaultAllocatorGuard guard(&localDefaultTa);

      int numdels = 0;

      {
          B *b_p = 0;
          A *a_p = b_p;
are legal expressions, then the statements
          bslma::ManagedPtr<A> a_mp1;
          bslma::ManagedPtr<B> b_mp1;

          assert(!a_mp1 && !b_mp1);

          a_mp1 = b_mp1;      // conversion assignment of nil ptr to nil
          assert(!a_mp1 && !b_mp1);

          B *b_p2 = new (localDefaultTa) B(&numdels);
          bslma::ManagedPtr<B> b_mp2(b_p2);    // default allocator
          assert(!a_mp1 && b_mp2);

          a_mp1 = b_mp2;      // conversion assignment of non-nil ptr to nil
          assert(a_mp1 && !b_mp2);

          B *b_p3 = new (localTa) B(&numdels);
          bslma::ManagedPtr<B> b_mp3(b_p3, &localTa);
          assert(a_mp1 && b_mp3);

          a_mp1 = b_mp3;      // conversion assignment of non-nil to non-nil
          assert(a_mp1 && !b_mp3);

          a_mp1 = b_mp3;      // conversion assignment of nil to non-nil
          assert(!a_mp1 && !b_mp3);

          // constructor conversion init with nil
          bslma::ManagedPtr<A> a_mp4(b_mp3, b_mp3.get());
          assert(!a_mp4 && !b_mp3);

          // constructor conversion init with non-nil
          B *p_b5 = new (localTa) B(&numdels);
          bslma::ManagedPtr<B> b_mp5(p_b5, &localTa);
          bslma::ManagedPtr<A> a_mp5(b_mp5, b_mp5.get());
          assert(a_mp5 && !b_mp5);
          assert(a_mp5.get() == p_b5);

          // constructor conversion init with non-nil
          B *p_b6 = new (localTa) B(&numdels);
          bslma::ManagedPtr<B> b_mp6(p_b6, &localTa);
          bslma::ManagedPtr<A> a_mp6(b_mp6);
          assert(a_mp6 && !b_mp6);
          assert(a_mp6.get() == p_b6);

          struct S {
              int d_i[10];
          };

          assert(200 == numdels);
      }

      assert(400 == numdels);
  } // implicitCastingExample()
Explicit Conversion:
Through "aliasing", a managed pointer of any type can be explicitly converted to a managed pointer of any other type using any legal cast expression. For example, to static-cast a managed pointer of type A to a managed pointer of type B, one can simply do the following:
  void explicitCastingExample() {

      bslma::ManagedPtr<A> a_mp;
      bslma::ManagedPtr<B> b_mp1(a_mp, static_cast<B *>(a_mp.get()));
or even use the less safe "C"-style casts:
      bslma::ManagedPtr<B> b_mp2(a_mp, (B *)(a_mp.get()));

  } // explicitCastingExample()
Note that when using dynamic cast, if the cast fails, the target managed pointer will be reset to an unset state, and the source will not be modified. Consider for example the following snippet of code:
  void processPolymorphicObject(bslma::ManagedPtr<A> aPtr,
                                bool *castSucceeded)
  {
      bslma::ManagedPtr<B> bPtr(aPtr, dynamic_cast<B *>(aPtr.get()));
      if (bPtr) {
          assert(!aPtr);
          *castSucceeded = true;
      }
      else {
          assert(aPtr);
          *castSucceeded = false;
      }
  }
If the value of aPtr can be dynamically cast to B * then ownership is transferred to bPtr; otherwise, aPtr is to be modified. As previously stated, the managed object will be destroyed correctly regardless of how it is cast.
Example 5: Inplace Object Creation:
Suppose we want to allocate memory for an object, construct it in place, and obtain a managed pointer referring to this object. This can be done in one step using two free functions provided in bslma::ManagedPtrUtil.
First, we create a simple class clearly showing the features of these functions. Note that this class does not define the bslma::UsesBslmaAllocator trait. It is done intentionally for illustration purposes only, and definitely is not recommended in production code. The class has an elided interface (i.e., copy constructor and copy-assignment operator are not included for brevity):
  class String {
      // Simple class that stores a copy of a null-terminated C-style string.

    private:
      // DATA
      char             *d_str_p;    // stored value (owned)
      bslma::Allocator *d_alloc_p;  // allocator to allocate any dynamic
                                    // memory (held, not owned)

    public:
      // CREATORS
      String(const char *str, bslma::Allocator *alloc)
          // Create an object having the same value as the specified 'str'
          // using the specified 'alloc' to supply memory.
      : d_alloc_p(alloc)
      {
          assert(str);
          assert(alloc);

          std::size_t length = std::strlen(str);

          d_str_p = static_cast<char *>(d_alloc_p->allocate(length + 1));
          std::strncpy(d_str_p, str, length + 1);
      }

      ~String()
          // Destroy this object.
      {
          d_alloc_p->deallocate(d_str_p);
      }

      // ACCESSORS
      bslma::Allocator *allocator() const
          // Return a pointer providing modifiable access to the allocator
          // associated with this 'String'.
      {
          return d_alloc_p;
      }
  };
Next, we create a code fragment that will construct a managed String object using the default allocator to supply memory:
  void testInplaceCreation()
  {
Suppose we want to have a different allocator supply memory allocated by the object:
      bslma::TestAllocator ta;
      bsls::Types::Int64   testBytesInUse = ta.numBytesInUse();

      assert(0 == testBytesInUse);

      bslma::TestAllocator         da;
      bslma::DefaultAllocatorGuard dag(&da);
      bsls::Types::Int64           defaultBytesInUse = da.numBytesInUse();

      assert(0 == defaultBytesInUse);
Then, create a string to copy:
      const char *STR        = "Test string";
      const int   STR_LENGTH = static_cast<int>(std::strlen(STR));
Next, dynamically create an object and obtain the managed pointer referring to it using the bslma::ManagedPtrUtil::makeManaged function:
      {
          bslma::ManagedPtr<String> stringManagedPtr =
                        bslma::ManagedPtrUtil::makeManaged<String>(STR, &ta);
Note that memory for the object itself is supplied by the default allocator, while memory for the copy of the passed string is supplied by another allocator:
          assert(static_cast<int>(sizeof(String)) <= da.numBytesInUse());
          assert(&ta == stringManagedPtr->allocator());
          assert(STR_LENGTH + 1 == ta.numBytesInUse());
      }
Then, make sure that all allocated memory is successfully released after managed pointer destruction:
      assert(0 == da.numBytesInUse());
      assert(0 == ta.numBytesInUse());
If you want to use an allocator other than the default allocator, then the allocateManaged function should be used instead:
      bslma::TestAllocator oa;
      bsls::Types::Int64   objectBytesInUse = oa.numBytesInUse();
      assert(0 == objectBytesInUse);

      {
          bslma::ManagedPtr<String> stringManagedPtr =
               bslma::ManagedPtrUtil::allocateManaged<String>(&oa, STR, &ta);

          assert(static_cast<int>(sizeof(String)) <= oa.numBytesInUse());
          assert(&ta == stringManagedPtr->allocator());
          assert(STR_LENGTH + 1 == ta.numBytesInUse());
          assert(0 == da.numBytesInUse());
      }

      assert(0 == da.numBytesInUse());
      assert(0 == ta.numBytesInUse());
      assert(0 == oa.numBytesInUse());
  }
Next, let's look at a more common scenario where the object's type uses bslma allocators. In that case allocateManaged implicitly passes the supplied allocator to the object's constructor as an extra argument in the final position.
The second example class almost completely repeats the first one, except that it explicitly defines the bslma::UsesBslmaAllocator trait:
  class StringAlloc {
      // Simple class that stores a copy of a null-terminated C-style string
      // and explicitly claims to use 'bslma' allocators.

    private:
      // DATA
      char             *d_str_p;    // stored value (owned)
      bslma::Allocator *d_alloc_p;  // allocator to allocate any dynamic
                                    // memory (held, not owned)

    public:
      // TRAITS
      BSLMF_NESTED_TRAIT_DECLARATION(StringAlloc, bslma::UsesBslmaAllocator);

      // CREATORS
      StringAlloc(const char *str, bslma::Allocator *basicAllocator = 0)
          // Create an object having the same value as the specified 'str'.
          // Optionally specify a 'basicAllocator' used to supply memory.  If
          // 'basicAllocator' is 0, the currently installed default allocator
          // is used.
      : d_alloc_p(bslma::Default::allocator(basicAllocator))
      {
          assert(str);

          std::size_t length = std::strlen(str);

          d_str_p = static_cast<char *>(d_alloc_p->allocate(length + 1));
          std::strncpy(d_str_p, str, length + 1);
      }

      ~StringAlloc()
          // Destroy this object.
      {
          d_alloc_p->deallocate(d_str_p);
      }

      // ACCESSORS
      bslma::Allocator *allocator() const
          // Return a pointer providing modifiable access to the allocator
          // associated with this 'StringAlloc'.
      {
          return d_alloc_p;
      }
  };
Then, let's create two managed objects using both makeManaged and allocateManaged:
  void testUsesAllocatorInplaceCreation()
  {
      bslma::TestAllocator ta;
      bsls::Types::Int64   testBytesInUse = ta.numBytesInUse();

      assert(0 == testBytesInUse);

      bslma::TestAllocator         da;
      bslma::DefaultAllocatorGuard dag(&da);
      bsls::Types::Int64           defaultBytesInUse = da.numBytesInUse();

      assert(0 == defaultBytesInUse);

      const char *STR        = "Test string";
      const int   STR_LENGTH = static_cast<int>(std::strlen(STR));
Note that we need to explicitly supply the allocator's address to makeManaged to be passed to the object's constructor:
      {
          bslma::ManagedPtr<StringAlloc> stringManagedPtr =
                   bslma::ManagedPtrUtil::makeManaged<StringAlloc>(STR, &ta);

          assert(static_cast<int>(sizeof(String)) <= da.numBytesInUse());
          assert(&ta == stringManagedPtr->allocator());
          assert(STR_LENGTH + 1 == ta.numBytesInUse());
      }
But the supplied allocator is implicitly passed to the constructor by allocateManaged:
      {
          bslma::ManagedPtr<StringAlloc> stringManagedPtr =
               bslma::ManagedPtrUtil::allocateManaged<StringAlloc>(&ta, STR);

          assert(static_cast<int>(sizeof(String)) + STR_LENGTH + 1 <=
                                                         ta.numBytesInUse());
          assert(&ta == stringManagedPtr->allocator());
          assert(0 == da.numBytesInUse());
      }
Finally, make sure that all allocated memory is successfully released after the managed pointers (and the objects they manage) are destroyed:
      assert(0 == da.numBytesInUse());
      assert(0 == ta.numBytesInUse());
  }