Using a Function to Create Objects (a.k.a. Factory Pattern)
Problem
Instead of creating a heap object with new, you need a function (member or standalone) to do the creation for you so that the type of the object being created is decided dynamically. This sort of behavior is what the Abstract Factory design pattern achieves.
Solution
You have a couple of choices here. You can:
- Have the function create an instance of the object on the heap, and return a pointer to the object (or update a pointer that was passed in with the new object's address)
- Have the function create and return a temporary object
Example 8-2 shows how to do both of these. The Session class in the example could be any class that you don't want application code to create directly (i.e., with new), but rather you want creation managed by some other class; in this example, the managing class is SessionFactory.
Example 8-2. Functions that create objects
#include class Session {}; class SessionFactory { public: Session Create( ); Session* CreatePtr( ); void Create(Session*& p); // ... }; // Return a copy of a stack object Session SessionFactory::Create( ) { Session s; return(s); } // Return a pointer to a heap object Session* SessionFactory::CreatePtr( ) { return(new Session( )); } // Update the caller's pointer with the address // of a new object void SessionFactory::Create(Session*& p) { p = new Session( ); } static SessionFactory f; // The one factory object int main( ) { Session* p1; Session* p2 = new Session( ); *p2 = f.Create( ); // Just assign the object returned from Create p1 = f.CreatePtr( ); // or return a pointer to a heap object f.Create(p1); // or update the pointer with the new address }
Discussion
Example 8-2 shows a few different ways to write a function that returns an object. You may want to do this instead of using new if the object being allocated is coming from a pool, is tied to hardware, or you want destruction of the objects to be managed by something other than the caller. There are many reasons to use this approach (which is why, incidentally, there is a design pattern for it); I have given only a few. Thankfully, implementation of the Factory pattern in C++ is straightforward.
Returning the address of a new heap object or updating a reference to a pointer argument are the most common ways to do this. Their implementation is shown in Example 8-2, and it is trivial, so there is no more to explain here. Less common, however, is returning an entire object from a function, most likely because it brings some caveats.
Returning a temporary object works by creating the object on the stack within the body of the function. When the function returns, the compiler copies the data from the temporary object to another temporary object that is actually returned by the function. Finally, the object in the calling function is assigned the value of the temporary object with its assignment operator. What this means is that two objects are actually created: the object in the factory function, and a temporary object that is returned from the function, and then its contents are copied to the target object. This is a lot of extra copying (although the compiler may optimize the temporary object away), so be aware of what's going on especially if you're working with large objects or frequent calls to this factory member function.
Also, this technique of copying a temporary object only works for objects that can behave as value objects, meaning that when one is copied the new version is equivalent to the original. For most objects this makes sense, but for others it doesn't. For example, consider creating an object of a class that listens on a network port. When you instantiate the object, it may begin listening on the target port, so you don't want to copy it to a new object because then you will have two objects attempting to listen on the same port. In this case, you would want to return the address of a heap object.
You should also take a look at Recipe 8.12 if you are writing a function or member function to create objects. With function templates, you can write a single function that returns a new object of any type. For example:
template T* createObject( ) { return(new T( )); } MyClass* p1 = createObject( ); MyOtherClass* p2 = createObject( ); // ...
This approach is handy if you want a single factory function to be able to create objects of any number of classes (or a group of related classes) in the same way, without having to write a redundant factory function multiple times.
See Also
Recipe 8.12