When I wrote about Class Construction, I noted that of all the incorrect arguments about constructors not being sufficient, there were a handful of cases that are actually correct. While not an exhaustive list, here are some cases that spring to mind:
- If you are creating a class hierarchy wherein a base class defines a number of virtual functions that subclasses are meant to implement (like
on_create
). It’s not unreasonable to try calling a virtual function in the base class constructor and expect a subclass function to run. Unfortunately this won’t work. The v-table won’t be set up yet, so attempting to call a virtual function will in turn call the base class’ implementation. - If you want to enforce HOW your objects are constructed. While it’s not possible to force all your classes to only be constructed on the stack, it is possible to force clients to only create objects as smart pointers (or raw pointers if you’re a big fan of resource leaks).
In both of these cases it won’t work to simply expect your clients to use the regular constructor. For case 2 you could solve this by adding documentation that says “please don’t create these on the stack” but if your code depends on developers actually reading the comments then it’s already doomed. You could solve case 1 by using an initialization function, but then you’d be totally ignoring all the great advice in the post that told you why that’s a horrible idea. So what’s left? This is a perfect situation for a static creation function, which looks like this:
class Some_class { public: static std::shared_ptr<Some_class> create(); };
So what’s going on here? Well, the first thing to notice is that it’s a static
function, which is a pretty important aspect to this technique as it means you can call the function without having created an instance of the object (because if you need the object to exist before you can create it, it’s probably not gonna work). The next thing to notice is that I have control over the return type, which in this case is a shared_ptr. You can imagine other return types (or even void if you need to enforce that the object must be created into some global store that you access in some other way).
At this point you may be pointing out that while this is all fine and dandy, there’s still nothing to stop anyone from just creating an instance of Some_class directly and avoiding the create function. You’d be right. The second half to this trick is that you have to make your constructor private. It will still be accessible inside the create function, but it won’t allow users to create your object any other way. So here’s a more complete example:
class Some_class { public: ~Some_class(); static std::shared_ptr<Some_class> create() { std::shared_ptr<Some_class> ptr( new Some_class); ptr->some_kind_of_initialization(); ptr->on_create(); return ptr; } private: Some_class(); Some_class( const Some_class& ); Some_class& operator=( const Some_class& ); };
I don’t generally put the code in the actual header, but it makes the example easier to read. Notice that along with the constructor I also made the copy constructor and operator= all private. There is now no way to create one of these things without using the static create function. Here’s how you’d have to make one:
std::shared_ptr<Some_class> ptr = Some_class::create(); // OK Some_class stack_instance; // won't compile Some_class copy_construct( *ptr ); // won't compile
Pretty sporty, huh? Take some time to soak this in, controlling an object’s construction is awesome and definitely needs to be a part of your toolkit.
Now you may also have heard about factories. A factory is a lot like a static creation function, but it exists as a separate class, adding a level of abstraction. I even had a co-worker who used to go on about how factories should actually be interfaces, so you could have entire hierarchies of factories that you could swap out to get different kind of creation functions. This all sounds well and good and impressive and fancy, but in practice I’ve never been in a situation where a static create function wasn’t good enough. Class factories always seem like going too complex for me. But as I’ve stated before, I’m not terribly clever, so if you really want to create a factory (or an entire inheritance tree of factories) then go right ahead. Just make sure you make your object’s constructors private to ensure people don’t just bypass the whole thing.
Some bullet points:
- Occasionally (rarely) a constructor is not sufficient
- Create a static function called “create” that does exactly that
- Make your object’s other construction functions private
- You can use class factories if you want to be a smarty pants