Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-04

Constructors & Destructors 101

11 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Richard Ward

I am a little bit unclear about certain things on class constructors and destructors.

From what I read in the language reference guide (chapter 6) and runtime utilities document (chapter 29):

1) All classes descend from TObject; even declaring a new class without using TObject as a qualifier.

2) TObject.Create is static (not declared virtual)

3) TObject.Destroy is declared virtual

4) Under most circumstances one should call the Free method to release an object since it checks for a nil pointer first. This method then calls the destructor (Destroy) which, after executing your own clean up code if any, will then release the memory. However, the object memory reference will not be set to nil. Testing this out confirms this and the variable will still contain the memory address of the previously allocated memory block even though the memory block pointed to by the variable is not usable for anything. To ensure that the address is set to nil, one should call the "global" sysutil function FreeAndNil , (chapter 30 RTL doc). This routine sets the address to nil before calling the free method which then calls the destructor.

5) Although I can't remember reading it anywhere, I've noticed from my own demo programs that the addresses of objects are initially set to nil.

If I have all of the above correct, then I have the following questions:

A) The documentation says that for the create constructor:

(quote}
Description: Create creates a new instance of TObject. Currently it does nothing. It is also not virtual, so there is in principle no need to call it directly.
{unquote}

What is it meant by: "no need to call [create] directly?" How do you invoke the constructor without calling it? ... and ... Why is create not virtual and the destroy destructor is?

B) The documentation says that for the destroy destructor:

(quote}
Description: Destroy is the destructor of TObject. It will clean up the memory assigned to the instance. Descendent classes should override destroy if they want to do additional clean-up. No other destructor should be implemented.
{unquote}

What is it meant by: "No other destructor should be implemented?" ...and... Does it do "something" while the Create constructor doesn't?

If you had two different create constructors (for whatever reason), might you not also need two different destroy destructors? What problems might you get into if you did?

C) Just out of curiosity, am wondering why FreeAndNil is global procedure instead of a method/destructor of TObject. I am guessing it is for compatibility with Delphi which may or may not have a reason?

I had other curiosity questions but found a nice web page which covers the Delphi Object Model.

http://oreilly.com/catalog/delphi/chapter/ch02.html

I have gotten several basic programs to run without worrying about these questions, but wanted to make sure I am not going to get into trouble later with some subtle problem cropping up. thanks for any information on this. - ROW

Reply by email

leledumbo
to Richard Ward
What is it meant by: "no need to call [create] directly?" How do you invoke the constructor without calling it? ... and ... Why is create not virtual and the destroy destructor is?

Often in other OOP language, you need to call parent constructor before doing anything else. In OP case, you don't since it doesn't do anything. Create isn't virtual because, as stated before, it doesn't do anything therefore no relation with descendant constructor(s) needed. Destroy is because it does "soemthing" (read below).

What is it meant by: "No other destructor should be implemented?" ...and... Does it do "something" while the Create constructor doesn't?

No other destructor should be implemented if you don't need to do any additional cleanup. It does "something", that is freeing memory assigned for the instance while create doesn't do anything.

If you had two different create constructors (for whatever reason), might you not also need two different destroy destructors? What problems might you get into if you did?

Only if it needs two different way to cleanup. No problems if you did, I often have overloaded constructors in classes I made (which of course only calls the one with most arguments).

Just out of curiosity, am wondering why FreeAndNil is global procedure instead of a method/destructor of TObject. I am guessing it is for compatibility with Delphi which may or may not have a reason?

It is indeed for Delphi compatibility. Other possible reason: It can be used by non-class types (i.e. Pointers) that do heap allocation.

View this message in context: http://www.nabble.com/Constructors---Destructors-101-tp22860474p22864567.html Sent from the Free Pascal - General mailing list .

Reply by email

Jonas Maebe
to Richard Ward: I am a little bit unclear about certain things on class constructors and destructors.…
Richard Ward wrote
C) Just out of curiosity, am wondering why FreeAndNil is global procedure instead of a method/destructor of TObject.

If you have:

var
   c: tsomeclass;
begin
   c:=c.tsomeclass.create;
   c.freeandnil
end;

then freeandnil would get, as first (hidden) parameter, the self pointer. However, this self pointer is a value parameter. So it would only contain the value of "c", not its address. Hence, there is no way to set "c" to nil inside the freeandnil method (while setting the instance variable to nil is the entire reason for using freeandnil).

Jonas

Reply by email

Jonas Maebe
to Richard Ward: I am a little bit unclear about certain things on class constructors and destructors.…
Richard Ward wrote
5) Although I can't remember reading it anywhere, I've noticed from my own demo programs that the addresses of objects are initially set to nil.

That is not correct. All global variables (classes/objects or not) are initialized to 0/nil, but local variables aren't (except in case of reference-counted types). Moreover, the contents of a class instance are also zeroed when creating it.

Jonas

Reply by email

Jonas Maebe
to Richard Ward: I am a little bit unclear about certain things on class constructors and destructors.…
Richard Ward wrote 10 lines
A) The documentation says that for the create constructor: (quote} Description: Create creates a new instance of TObject. Currently it does nothing. It is also not virtual, so there is in principle no need to call it directly. {unquote} What is it meant by: "no need to call [create] directly?" How do you invoke the constructor without calling it?

I think what is meant, is that if you create a direct subclass of TObject, there is no need to call TObject's create constructor (e.g., via "inherited create;") from your own constructors. It doesn't hurt if you do it of course, and may be good practice to account for future situations where the parent class may change.

... and ... Why is create not virtual and the destroy destructor is?

Because when creating a class instance, you usually know the exact class type of that instance (e.g., TObject.create -> you know that the created instance type will be of the type TObject). This only changes if you use class reference types, but if you use those you can still declare your own virtual constructors.

Conversely, many routines that free a class instance, have no idea about the exact type of that instance (such as FreeAndNil: all it knows is that the instance inherits from TObject). So to make sure that all resources allocated by those class types are properly freed, the destructor almost has to be virtual (since otherwise, only TObject's dummy destructor would be called by, e.g., FreeAndNil).

Show quoted text 10 lines
B) The documentation says that for the destroy destructor: (quote} Description: Destroy is the destructor of TObject. It will clean up the memory assigned to the instance. Descendent classes should override destroy if they want to do additional clean-up. No other destructor should be implemented. {unquote} What is it meant by: "No other destructor should be implemented?"

It means that you should not add "destructor
my_peculiarly_named_destructor; virtual;" to your own classes. The reason is that TObject's free method, which is used by pretty much all code out there, is hardcoded to call "destroy". So if your destructor is named differently, a lot of existing code will not properly destroy instances of your class type.

...and... Does it do "something" while the Create constructor doesn't?

No, TObject's default destructor does not do anything either.

If you had two different create constructors (for whatever reason), might you not also need two different destroy destructors?

No, the default destructor should always free all resources, regardless of how the class instance was created. Otherwise, it would also make your code more complex, because throughout the code you would have to track how the instance was created, so that in the end you could call the correct destructor.

What problems might you get into if you did?

See above.

Jonas

Reply by email

Flávio Etrusco
to Richard Ward: I am a little bit unclear about certain things on class constructors and destructors.…
C) Just out of curiosity, am wondering why FreeAndNil is global procedure instead of a method/destructor of TObject.   I am guessing it is for compatibility with Delphi which may or may not have a reason?

A method could not act the way FreeAndNil works (zeroing a local pointer variable). It could be a static/class method, but this would just add noise to the code.
BTW FreeAndNil can only on TObject and descendants.

-Flávio

Reply by email

Bart
to Jonas Maebe: I think what is meant, is that if you create a direct subclass of TObject, there is no…
Jonas Maebe wrote 5 lines
I think what is meant, is that if you create a direct subclass of TObject, there is no need to call TObject's create constructor (e.g., via "inherited create;") from your own constructors. It doesn't hurt if you do it of course, and may be good practice to account for future situations where the parent class may change.

I often wondered abou that.

So if i understand correctly:

Say I have

Type
  TFoo = class;
  private
    fSomeField: Integer;
  public
    constructor Create;
  end;

then

constructor  TFoo.Create
begin
  Inherited Create;
  fSomeField := -1;
end;

would in essence be equal to

constructor  TFoo.Create
begin
  fSomeField := -1;
end;

Since TOblect.Create "does nothing".

Bart

Reply by email

Jonas Maebe
to Bart
Bart wrote 28 lines
So if i understand correctly: Say I have Type TFoo = class; private fSomeField: Integer; public constructor Create; end; then constructor TFoo.Create begin Inherited Create; fSomeField := -1; end; would in essence be equal to constructor TFoo.Create begin fSomeField := -1; end; Since TOblect.Create "does nothing".

Yes.

Jonas

Reply by email

Doug Chamberlin
to Bart: I often wondered abou that. So if i understand correctly: Say I have Type TFoo = class;…
Bart wrote 37 lines
On 4/3/09, Jonas Maebe wrote: > I think what is meant, is that if you create a direct subclass of TObject, > there is no need to call TObject's create constructor (e.g., via "inherited > create;") from your own constructors. It doesn't hurt if you do it of > course, and may be good practice to account for future situations where the > parent class may change. I often wondered abou that. So if i understand correctly: Say I have Type TFoo = class; private fSomeField: Integer; public constructor Create; end; then constructor TFoo.Create begin Inherited Create; fSomeField := -1; end; would in essence be equal to constructor TFoo.Create begin fSomeField := -1; end; Since TOblect.Create "does nothing".

Essentially, yes.

However, you may create subtle, lurking bugs if you omit that call and later refactor your code. For example, if you later change

   type
     TFoo = class

to

   type
     TFoo = class(TSomeClass)

and TSomeClass has some important work done in it's Create constructor. If you properly called "inherited Create;" now that important stuff will get done, just the way you want it to!

<emphasis>
So, the big lesson here is to stick to the proper structure even though you may have outside knowledge that the form you should follow is not strictly necessary in all cases.
</emphasis>

Reply by email

Bart
to Doug Chamberlin
Doug Chamberlin wrote 17 lines
Essentially, yes. However, you may create subtle, lurking bugs if you omit that call and later refactor your code. For example, if you later change type TFoo = class to type TFoo = class(TSomeClass) and TSomeClass has some important work done in it's Create constructor. If you properly called "inherited Create;" now that important stuff will get done, just the way you want it to!

Despite the fact that I've been programming in Pascal and Delphi for quite some time now, I've only recently started looking into and writing components (thanks to Lazarus!).
Most tutorials on that topic started out with TComponent decendents, where invoking the inherited contsructor is eminent. I've since seen several TObjet derived classes, some of them invoking inherited Create, some not. I never understood the difference and this made me insecure wether my own (small) TObject derived classes (I always invoked inherited Create in my constructor) were potentially flawed.

Show quoted text 5 lines
<emphasis> So, the big lesson here is to stick to the proper structure even though you may have outside knowledge that the form you should follow is not strictly necessary in all cases. </emphasis>

I agree.

Thanks for clearing things up to me.

Bart

Reply by email

Nikolay Nikolov
to Jonas Maebe: I think what is meant, is that if you create a direct subclass of TObject, there is no…
Jonas Maebe wrote 8 lines
> If you had two different create constructors (for whatever reason), > might you not also need two different destroy destructors? No, the default destructor should always free all resources, regardless of how the class instance was created. Otherwise, it would also make your code more complex, because throughout the code you would have to track how the instance was created, so that in the end you could call the correct destructor.

I think there's something even more subtle than that. If a constructor blows up and raises an exception, the destructor Destroy is called automatically to clean up the partially created class, before the exception is handled. So, it's not just .free that is hardcoded to call destroy. IOW it is an extremely bad idea to have a destructor other than Destroy for classes and IMO shouldn't be allowed by the language. However, having multiple constructors is perfectly fine, as long as the destructor is only one.

Reply by email