NOTE
Derived classes only override Initialize() if they want to add their own post-construction initialization calls.
For example, sometimes during construction you need to create items that can't be created until the most derived class is constructed. Suppose you have a guard element of a linked list, where overridding DoMakeLink() in a derived class can change the type of the link element. By postponing the Initialize() call to the most derived constructor, you call the proper DoMakeLink.
Never require the client to call a separate virtual Initialize() function to finish initialization after constructing all bases. Requiring the client to remember this is extremely error prone; if the client forgets, the object will not be properly constructed.
Lazy evaluation. In every client function, check a flag (set to FALSE in the constructor); then complete initialization by calling the virtual initializer at that time. This works well only in a limited number of cases.
External/internal constructors. Classes in the hierarchy affected by the need for virtual initialization should have both internal (protected) and external (public) constructors. The internal constructors don't call virtual Initialize(). The external constructors call the internal one (actually, a shared private function), as well as the internal constructors of all bases. The external constructors then call the virtual Initialize(). This is error prone too, but only for subclasses as opposed to all clients.
Clients call the external constructor, and derived classes call the internal constructors of their base classes. This method calls the virtual Initialize() for the most derived class only, the one the client constructed directly.
The cost of failing
to call Initialize()
The overhead of checking for failure to call Initialize() is comparable to or greater than the following alternative schemes for achieving the same end:
Use virtual base class semantics. Include a virtual base class in the affected class hierarchy, and make use of the fact that only the outermost call to that base class' constructor is used. The Taligent Application Environment will include a class similar to this: class Base {
public:
Base() {InitBase(); Initialize();};
protected:
virtual void Initialize();
enum {kInternal} InternalMarker;
Base(InternalMarker) {InitBase();};
private:
void InitBase();
};
class Derived: public Base {
public:
Derived() : Base(kInternal) {InitDerived(); Initialize(););
protected:
virtual void Initialize();
Derived(InternalMarker) {InitDerived();};
private:
void InitDerived();
};
#include <typeinfo.h>
#include <stdio.h>
class VInitialize {
public:
VInitialize(const typeinfo& t) : fCompleteType(t) {}
void CheckForInitialize() { if (typeid(*this) == fCompleteType) Initialize(); }
void CheckForFinalize() { if (typeid(*this) == fCompleteType) Finalize(); }
virtual void Initialize() = 0;
virtual void Finalize() = 0;
private:
const typeinfo& fCompleteType;
};
class Base {
public:
Base() {}
};
class JoesParent : public Base, public virtual VInitialize {
public:
JoesParent() : Base(), VInitialize(typeid(JoesParent)) {
CheckForInitialize(); // normal initialization
};
~JoesParent() {
CheckForFinalize(); // normal finalization
}
protected:
virtual void Initialize(); // only called if this is the complete class
virtual void Finalize(); // only called if this is the complete class
};
class JoeClass: public JoesParent {
public:
JoeClass() : JoesParent(), VInitialize(typeid(JoeClass)) {
CheckForInitialize(); // normal initialization
}
~JoeClass() {
CheckForFinalize(); // normal finalization
}
protected:
virtual void Initialize(); // only called if this is the complete class
virtual void Finalize(); // only called if this is the complete class
};
void JoesParent::Initialize() { puts("Called JoesParent::Initialize()"); }
void JoesParent::Finalize() { puts("Called JoesParent::Finalize()"); }
void JoeClass::Initialize() { puts("Called JoeClass::Initialize()"); }
void JoeClass::Finalize() { puts("Called JoeClass::Finalize()"); }
int main() {
puts("--- creating a JoesParent ---");
JoesParent *x = new JoesParent;
puts("--- creating a JoeClass ---");
JoeClass *y = new JoeClass;
puts("--- deleting a JoesParent ---");
delete x;
puts("--- deleting a JoeClass ---");
delete y;
return 0;
}
--- creating a JoesParent ---
Called JoesParent::Initialize()
--- creating a JoeClass ---
Called JoeClass::Initialize()
--- deleting a JoesParent ---
Called JoesParent::Finalize()
--- deleting a JoeClass ---
Called JoeClass::Finalize()
[Contents]
[Previous]
[Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Generated with WebMaker