Yesterday I posted an observation on Google+, lamenting the lack of a compiler warning when code in an overridden method failed to call any inherited implementation. This simple oversight in an AfterConstruction override in a situation where the observed bug that arose (a memory leak) could just as easily have been the consequence of a more complex error on my part, caused me to spend a significant amount of time, hunting down the wrong bug.
Eric Grange suggested that AfterConstruction and BeforeDestruction was best forgotten. This was an interesting thought, but when I considered it I concluded that I had to disagree. Not only would it not help me avoid my problem had I ignored them, but doing so would merely have created new opportunities to introduce such time-wasting bugs.
AfterConstruction and BeforeDestruction fulfil a genuine need imho. Most especially I have found them of immense utility when creating frameworks. In a framework I find I often have a base class (or classes) from which further classes are intended to be derived, with (potentially) overridden constructors. Also quite often, the base class never-the-less requires some internal initialisation to reliably occur only after any overridden construction has been completed.
In the specific case that led to my initial post, the base class in question contained an interface reference counting bug fix I originally developed long before it was fixed in the VCL. Actually, it addresses a duo of bugs, one of which manifests during construction and the other during destruction. From memory (my Windows VM is not currently running so I can’t check the VCL source to be certain), the VCL now addresses the constructor side of things but not the destructor.
In any event, my base class incorporates a NewInstance override (effectively BeforeConstruction) and an accompanying AfterConstruction implementation that operated to ensure a positive reference count during the execution of those constructors. This addresses the situation where an object references itself using an interface during construction (e.g. passing a reference to itself to some other object which then discards it) resulting in the reference count “bouncing”, falling back to zero and thus destroying itself. A similar mechanism using BeforeDestruction ensures that “double-destruction” doesn’t occur if self is reference as an interface during destructor execution).
If AfterConstruction and BeforeDestruction didn’t exist (or you ignored them) then in such circumstances you would simply have to contrive your own exactly equivalent mechanism. This mechanism would still – by necessity – rely on virtual methods and overrides and so as far as I can see will still be vulnerable to the omission of calls to the inherited implementation.
But worse, without it being implemented in the base framework further steps would most likely be required to ensure that the alternate mechanism was correctly invoked. As far as I can see, ignoring the provided core mechanism and “rolling your own” would only compound the problem, increasing the potential to introduce a mistake rather than reducing it.
Setting aside the rights or wrongs of an AfterConstruction or BeforeDestruction mechanism in the specific, in the general case overriding a method and then neglecting to call “inherited” is a sufficiently rare occurrence that it would be worth warning about imho.
Furthermore, the compiler directive(s) required to then silence that warning would provide a self documenting and clear signal that the omission is indeed intentional, expected and indeed required (in those rare cases that it genuinely is).
Comments are now closed.