Use of 'this' in constructors

this in constructors

Some quick C++ advice today, but I think it generalizes to C# and Java too. If you do anything with the this pointer in your constructor – apart from dereference it – your design is probably missing a class.

There are a few dangers with using the this pointer in the constructor. For a start, weird stuff will happen if it ends up in the hands of code that attempts to call a virtual method: in C++ the instance changes type as each constructor in the inheritance hierarchy gets a shot at it, meaning that the same call will invoke different code depending on where we are in the construction process. In C# and Java you get a different sort of misery – virtual methods in a subclass can be invoked before their constructor has ever had a chance to initialize the subclass fields.

But I'd say the more important general problem is that it becomes much harder to reason about the class. It's no longer clear which methods can rely on the class's invariants being intact when they start, and it's no longer clear at which point in the constructor invariants should be established.

Concurrent access in destructors

The other side of this coin is destruction. If methods on your class can be invoked concurrently with its destructor something is fishy in your class! The most common scenario I've seen this is when the class manages a thread which it attempts to join during the destructor, and where the thread is calling methods on the class itself. This isn't obviously insane – with appropriate synchronization it can work and be safe – but I'd argue that it's still a really bad idea. For one thing, it all goes a bit wrong if you create a subclass – the concurrent calls will continue while the subclass destructor runs. (I'm not 100% sure, but I think it's probably undefined behaviour to invoke a virtual method on an instance concurrently with one of its subclass destructors finishing.) Even if you avoid subclassing, it gives you a messier destructor that's harder to reason about.

You're missing a class

The good news is that there is a general solution to these problems. If you have a class A that in its constructor wants to send its this pointer to a class B (figure 1), you can almost always solve the problem by splitting A into A1 and A2, with A2 a member of A1 (figure 2). The A1 constructor can now give B a pointer to A2. It might be a little tricky to figure out what goes in A1 and A2, but I think you'll find in the end the design becomes cleaner and your constructors and destructors will become less tangled and confusing.

Figure 1 – Before, the cycle of pain

Figure 2 – After, a pleasant pyramid

No comments:

Post a Comment