Rudy Velthuis [TeamB] wrote:
Quote
Just like what Liz said, that is wrong. It is not opposite, it just
not always the same. But generally, it is the same as in C++.
"It is the same apart from when it is not" is usually an accurate
statement, but not overly useful ;?
Delphi construction is quite different.
First, the memory to be occupied by your object is zeroed. This
'pre-constructor' has no equivalent in C++, but provides essential
guarantees later in the process. It also means all your data members
*are initialized* before you get into user-written code.
Then the most-derived ctor is called, and can do whatever it likes,
including not calling any base class ctor at all. After all, the
object was zero-initialized, so all data members are defined.
However, if it chooses to chain a base class ctor, it does so at a
moment of its choosing. Conventionally, but not necessarily first.
Base class ctors do not get the zero-initialization pre-constructor,
that is (for obvious reasons) fired at the most-derived level only. So
execution proceed directly to ctor body. During this ctor, virtual
functions will dispatch to the most derived type. That is the reason
everything is zero-initialized, to give you something to safely check.
Likewise, if you rely on certain data being available during virtual
calls - you can initialize those members before calling your base ctor.
This fundamentally changes the contract between base and derived
classes. Guarantees you rely on in C++ do not hold, likewise
additional guarantees are expected for the Delphi behaviour. This is
no accident as places in the VCL rely on this behaviour, whether a
given user chooses to code exclusively in the C++ style or not
(although if they do so, you wonder why they are using Delphi in the
first place, or at least crippling their ability to use it effectively)
Finally, after all ctors have finished executing, the AfterConstruction
virtual function is called. Again, their is no C++ equivalent.
So by user dilligence you can choose to treat Delphi construction as
C++ construction - by following conventions to never call virtual
functions (or functions that in turn call virtual functions), always
chaining you base class ctor first, never taking advantage of the
implicit zero-initialization of your memory, and never overriding the
AfterConstruction function. Oh, and never deriving from a 3rd party
base class that does not follow these rules as well.
Otherwise, accept the languages are different for a reason and use them
for their strengths.
One thing I was never entirely sure on is what happens in Delphi
destructors when a constructor throws an exception. Which destructors
are called, and in what sequence? Are dtors called for base classes
that were never constructed? Does destruction start with the
most-derived dtor first? The Delphi language guide I learned from
advised writing destructors to cope with partially initialized classes,
but ducked on the details of how you might do that.
Generally, I find the difference between the two models that are
'mostly the same' strikes when trying to write code that 'really works'
rather than 'mostly works'. At that point, you have to consider you
constructor/destructor model, and write code appropriately.
End of lecture!
--
AlisdairM(TeamB)