Board index » delphi » Sequence for a destructor

Sequence for a destructor

Is this the right code sequence for a destructor
that essentially frees and nils?

destructor TGameServer.Destroy;
begin
  FServer.destroy;
  inherited destroy;
  FServer := nil;
end;

Just making sure...TIA

 

Re:Sequence for a destructor


Quote
Nolen wrote:
> Is this the right code sequence for a destructor
> that essentially frees and nils?

> destructor TGameServer.Destroy;
> begin
>   FServer.destroy;
>   inherited destroy;
>   FServer := nil;
> end;

Instead of calling Destroy, call Free. If there is an exception in the
constructor, the FServer's value may be nil when the destructor gets
called. Calling Destroy on a nil reference will generate an access
violation, and another exception is the last thing you want while
already processing one. The Free method, on the other hand,
automatically protects against nil references. There is almost never an
occasion to ever call Destroy yourself.

I assume that FServer is a field of TGameServer, right? If so, then
there's no need to set it to nil. Since the entire object ceases to
exist on the very next line, it makes no difference what one of its
fields' values is anymore.

--
Rob

Re:Sequence for a destructor


Im Artikel <OEd79.126486$UU1.22188@sccrnsc03>, "Nolen" <nol...@attbi.com>
schreibt:

Quote
>Is this the right code sequence for a destructor
>that essentially frees and nils?

IMO not :-(

Quote
>destructor TGameServer.Destroy;
>begin
>  FServer.destroy;

Never call destroy, call Free instead!
Quote
>  inherited destroy;

This should be the last statement in the destructor.
Quote
>  FServer := nil;

Why here, why not after destroy?

Quote
>end;

DoDi

Re:Sequence for a destructor


Quote
"VBDis" <vb...@aol.com> wrote in message

news:20020817222328.28944.00000083@mb-cm.aol.com...

Quote
> Im Artikel <OEd79.126486$UU1.22188@sccrnsc03>, "Nolen" <nol...@attbi.com>
> schreibt:
> >destructor TGameServer.Destroy;
> >begin
> >  FServer.destroy;
> Never call destroy, call Free instead!

Agreed, in general. Free will call the *appropriate* virtual destructor for the
class.

Quote
> >  inherited destroy;
> This should be the last statement in the destructor.

Why? Calling "inherited destroy" only executes statements in the parent
destructors; it does not free any memory for the current object, so it is
*normally* safe to do this:

destructor TDerivedClass.Destroy;
begin
  inherited Destroy;
  FObject1.Free;
  FObject2.Free;
  .
  .
end;

This is not always the case, but it is good practice to write classes so that
destructor sequences *can* be arranged like this - no strange dependencies
between base/derived classes.

Cheers

Matthew
--
Matthew Skelton
Resonance Instruments Ltd.
http://www.resonance.co.uk/
mps@REMOVE_COLOUR.resonance.red.co.uk

Re:Sequence for a destructor


Quote
Matthew Skelton wrote in message ...
>"VBDis" <vb...@aol.com> wrote in message
>news:20020817222328.28944.00000083@mb-cm.aol.com...
>> Im Artikel <OEd79.126486$UU1.22188@sccrnsc03>, "Nolen"

<nol...@attbi.com>

Quote
>> schreibt:
>> >destructor TGameServer.Destroy;
>> >begin
>> >  FServer.destroy;
>> Never call destroy, call Free instead!

>Agreed, in general. Free will call the *appropriate* virtual destructor
for the
>class.

So will Destroy. This is a quality of virtual methods in
general, not of Free.

(Exceptions can be construed. But they'll never compile
without warnings.)

Groetjes,
Maarten Wiltink

Re:Sequence for a destructor


Im Artikel <ajtkp5$4d...@helle.btinternet.com>, "Matthew Skelton"
<mps@REMOVE_COLOUR.resonance.red.co.uk> schreibt:

Quote
>> >  inherited destroy;
>> This should be the last statement in the destructor.

>Why? Calling "inherited destroy" only executes statements in the parent
>destructors; it does not free any memory for the current object

It's not a matter of freing memory for the actual object, but you're calling
for trouble when you free any members, which might be used in inherited
destructors. Or when an inherited destructor destroys members, which are used
in subsequently called destructors.

A rule of thumb (and of symmetry):

If you have an constructor like:
  inherited
  create A
  create B
then the according destructor should do:
  free B
  free A
  inherited

DoDi

Re:Sequence for a destructor


Quote
"Maarten Wiltink" <maar...@kittensandcats.net> wrote in message

news:ak02ca$n4l$1@news1.xs4all.nl...

Quote
> >Agreed, in general. Free will call the *appropriate* virtual destructor
> > for the class.

> So will Destroy. This is a quality of virtual methods in
> general, not of Free.

Maarten

Apologies. Time to revise my understanding of Delphi. What I had thought was:

<thinks>
There is no guarantee that the *appropriate* destructor is called "Destroy".
Free will call the most derived destructor, but Destroy will call that most
derived destructor named Destroy.
</thinks>

However, upon test (see below) it seems that destructors *must* be called
Destroy if TObject.Free() is to see them. Having seen the src in System.pas, I
assumed that the compiler would see the keyword 'destructor' in a class
defintion and point TObject.vmtDestroy to that:

procedure TObject.Free;
{snip}
  CALL    dword ptr [ECX].vmtDestroy
{snip}
end;

But no, vmtDestroy only points to the most derived "Destroy" method.

mea culpa

Matthew

<test code>
type

  TBase = class(TObject)
  public
    destructor Destroy; override;
  end;

  TDerived = class(TBase)
  public
    { Methods }
    destructor Kill; virtual;
  end;
.
.
.
{ TDerived }

destructor TDerived.Kill;
begin
  // This is not called by TObject.Free :o(
  //
  MessageDlg('Inside TDerived.Kill!', mtInformation, [mbOK], 0);
  inherited Destroy;
end;

{ TBase }

destructor TBase.Destroy;
begin
  MessageDlg('Inside TBase.Destroy!', mtInformation, [mbOK], 0);
  inherited;
end;

</test code>

--
Matthew Skelton
Resonance Instruments Ltd.
http://www.resonance.co.uk/
mps@REMOVE_COLOUR.resonance.red.co.uk

Re:Sequence for a destructor


Quote
"VBDis" <vb...@aol.com> wrote in message

news:20020821114012.27039.00000038@mb-bk.aol.com...

Quote
> Im Artikel <ajtkp5$4d...@helle.btinternet.com>, "Matthew Skelton"
> <mps@REMOVE_COLOUR.resonance.red.co.uk> schreibt:

> >Why? Calling "inherited destroy" only executes statements in the parent
> >destructors; it does not free any memory for the current object

> It's not a matter of freing memory for the actual object, but you're calling
> for trouble when you free any members, which might be used in inherited
> destructors. Or when an inherited destructor destroys members, which are used
> in subsequently called destructors.

VBDis,

It seems dangerous to rely on inherited members inside a destructor. Each level
of the class hierarchy should deal only with members introduced at that level:

type
  class TBase = class(TObject)
  protected
    BaseStrings : TStrings;
  public
    .
    destructor Destroy; override;
  end;

  class TDerived = class(TBase)
  protected
    DerivedStrings : TStrings;
  public
    .
    destructor Destroy; override;
  end;

destructor TBase.Destroy;
begin
  inherited;
  BaseStrings.Free;
end;

destructor TBase.Destroy;
begin
  inherited;
  DerivedStrings.Free;
end;

This follows general OO rules of encapsulation etc. Inside a destructor, I'd
trust as little as possible, and certainly not the values of inherited members,
other than calling "inherited Destroy".

Matthew
--
Matthew Skelton
Resonance Instruments Ltd.
http://www.resonance.co.uk/
mps@REMOVE_COLOUR.resonance.red.co.uk

Re:Sequence for a destructor


Quote
"Matthew Skelton" <mps@REMOVE_COLOUR.resonance.red.co.uk> wrote in message
> This follows general OO rules of encapsulation etc. Inside a destructor,
I'd
> trust as little as possible, and certainly not the values of inherited
members,
> other than calling "inherited Destroy".

You are suggesting that an descendant should never include code that depends
on the exposed part of its ancestor. IMO this just isn't realistic. In fact
I would go so far as to say that it is entirely reasonable and correct for a
descendant to assume the existence of its ancestor during the descendant's
lifetime. Placing the inherited call at the beginning of the destructor
obviously makes this impossible.

The general rule of thumb for any resource allocation/deallocation that I
was taught and have always used is to release resources in the reverse order
of their allocation. Hence

construct 1
construct 2
. . .
construct N

destructN
. .
destruct 2
destruct 1

Re:Sequence for a destructor


Free always calls the virtual destructor Destroy. The reason for using Free
instead of a direct call to Destroy is to remove the need for code such as

    if Assigned (anInstanceReference)
    then anInstanceReference.Destroy;

It is the only reason for using Free instead of Destroy. In fact, all Free
does is the above.

If one has destructors other than Destroy, then at some point they will have
to cause a call to the inherited Destroy, otherwise their is no assurance
that the instance will be properly destroyed.

Re:Sequence for a destructor


Quote
Matthew Skelton wrote in message ...
>"Maarten Wiltink" <maar...@kittensandcats.net> wrote in message
>news:ak02ca$n4l$1@news1.xs4all.nl...
>> >Agreed, in general. Free will call the *appropriate* virtual destructor
>> > for the class.

>> So will Destroy. This is a quality of virtual methods in
>> general, not of Free.
>Apologies. Time to revise my understanding of Delphi. What I had thought

was:

That's okay. Save the apologies for when your faults are pointed
out and you do _not_ learn from it.

Quote
><thinks>
>There is no guarantee that the *appropriate* destructor is called
"Destroy".
>Free will call the most derived destructor, but Destroy will call that
most
>derived destructor named Destroy.
></thinks>

There _is_ no guarantee that the appropriate destructor is called
Destroy. It's just that generally, there is no other. This is not
an accident, either. TObject.Destroy is virtual, and that's not an
accident, either. It's meant to be the unique destructor.

Groetjes,
Maarten Wiltink

Re:Sequence for a destructor


Quote
"Maarten Wiltink" <maar...@kittensandcats.net> wrote in message

news:ak2rkp$6eu$1@news1.xs4all.nl...

Quote
> There _is_ no guarantee that the appropriate destructor is called
> Destroy. It's just that generally, there is no other. This is not
> an accident, either. TObject.Destroy is virtual, and that's not an
> accident, either. It's meant to be the unique destructor.

So destructors *may* be called something other than "Destroy" but much
functionality is lost. In the code I posted above, the virtual destructor named
"Kill" in the derived class was simply not called by TObject.Free - it seems
that TObject.Free will only call a destructor called "Destroy".

Can anyone confirm? The D5 OP Language Guide merely hints...

Thanks

Matthew
--
Matthew Skelton
Resonance Instruments Ltd.
http://www.resonance.co.uk/
mps@REMOVE_COLOUR.resonance.red.co.uk

Re:Sequence for a destructor


"Matthew Skelton" <mps@REMOVE_COLOUR.resonance.red.co.uk> skrev i melding
news:ak2sre$to$1@venus.btinternet.com...

Quote

> "Maarten Wiltink" <maar...@kittensandcats.net> wrote in message
> news:ak2rkp$6eu$1@news1.xs4all.nl...

> > There _is_ no guarantee that the appropriate destructor is called
> > Destroy. It's just that generally, there is no other. This is not
> > an accident, either. TObject.Destroy is virtual, and that's not an
> > accident, either. It's meant to be the unique destructor.

> So destructors *may* be called something other than "Destroy" but much
> functionality is lost. In the code I posted above, the virtual destructor
named
> "Kill" in the derived class was simply not called by TObject.Free - it
seems
> that TObject.Free will only call a destructor called "Destroy".

> Can anyone confirm? The D5 OP Language Guide merely hints...

That's correct. The thing about "destructor" is the automagical code
inserted by the compiler to actually call FreeInstance, etc..., the thing
about "destroy" is that that'?s the one that gets called in TObject.Free.
You're 100% free to create a parallell logic, e.g.:

destructor TMyObject.Kill;
begin
  GarbageList.Remove(Self);
  Destroy; //
end;

procedure TMyObject.Delete;
begin
  if Assigned(Self) then
    Kill;
end;

...but you need to call a destructor, though...

--
Bj?rge S?ther
bjorge@hahaha_itte.no

Re:Sequence for a destructor


Im Artikel <ak0eeh$i7...@venus.btinternet.com>, "Matthew Skelton"
<mps@REMOVE_COLOUR.resonance.red.co.uk> schreibt:

Quote
>It seems dangerous to rely on inherited members inside a destructor. Each
>level
>of the class hierarchy should deal only with members introduced at that
>level:

I fully agree :-)

But in comparison to the construction and destruction of a building, the
construction should work bottom-up, and the destruction top-down. Destructing
the first floor of a building will make collapse it at once, but not in an
orderly manner. Therefore the destruction of objects also should start at the
top level, and only after one level has been destroyed, the next lower level
(inherited) destructor should be called.

One also must consider that a destructor can do more than only free child
objects, which have been added at the same level. Whenever a virtual method may
be called during destruction, then IMO in Delphi the most derived version of
that method is called. (This behaviour is different from e.g. construction and
destruction in C++). Destroying lower level objects first will most certainly
prevent virtual methods to work properly :-(

In some cases, e.g. with circular references between parent and child objects,
special procedures may be required to prevent recursive destruction or other
race conditions. Then it may be necessary to set the reference to a child
object to nil, /before/ destroying the child object. Similar considerations
apply to constructors for sophisticated classes.

DoDi

Re:Sequence for a destructor


Quote
"VBDis" <vb...@aol.com> wrote in message

news:20020823061129.23530.00000303@mb-

Quote
> But in comparison to the construction and destruction of a building, the
> construction should work bottom-up, and the destruction top-down.
Destructing
> the first floor of a building will make collapse it at once, but not in an
> orderly manner . . .

Unless, of course, one is using explosives in a knowledgeable way. :)).
Go to page: [1] [2]

Other Threads