constructors

hi all,

i've got a few points, and this one is relatively easy, so i'm feeling generous.

[snip]

unit test;

interface

type
  Tgrounds=class(TForm)
    field_1:longint;
    field_2:longint;
    constructor make;
  end;

var
  grounds:Tgrounds;

implementation

constructor tgrounds.make;
begin
  if self=nil then
    inherited;
  field_1:=1;
  field_2:=2;
end;

end.

[snip]

unit test2;

interface

uses
  test;

implementation

const
  grounds:tgrounds=nil;

begin
  grounds.make;
end.
 
[snip]

so, when the constuctor is called, grounds is nil. but the 'inherited' line isn't called when self is checked for nil.

the thing is, self shows up in a watch window as nil.

what am i doin wrong?

tia

andrew

andruAsked:
Who is Participating?
 
LischkeConnect With a Mentor Commented:
Ah, I forgot, since Make is also a constructor you can create it this way:

function Grounds: TGrounds;

begin
  if FGrounds = nil then FGrounds := TGrounds.Make;
  Result := FGrounds;
end;

Make sure the ancestor's constructors are called to init their data:

constructor TGrounds.Make;

begin
  Create(nil);
end;

Ciao, Mike
0
 
brunoheCommented:
Try

type
  Tgrounds=class(TForm)
    field_1:longint;
    field_2:longint;
  public  
    constructor make;
  end;
...
...
constructor tgrounds.make;
begin
  inherited Create(nil);
end;

end.

0
 
brunoheCommented:
Aehm... sorry. The reason: The compiler doesn't know the connection between your Make and the standard 'Create' procedure when you simply call 'inherited'. Instead when you're using 'inherited Create(nil)' you can redirect the call to the standard constructor of TForm.

That's it.
0
Receive 1:1 tech help

Solve your biggest tech problems alongside global tech experts with 1:1 help.

 
MadshiCommented:
Brunohe is right.

Two more hints:

(1) In the constructor *ALWAYS* call the inherited constructor. Don't ask something like this:

  if self=nil then
    inherited;
 
(2) "self" is not "grounds". "self" is *never* nil. "grounds" can be nil. Look, you can do this:

  TBitmap.Create.Free;

There's no variable involved at all. Nevertheless "self" is not nil in the TBitmap methods.

Regards, Madshi.
0
 
rwilson032697Commented:
I wonder if self in a constructor is a reference to the class prior the inherited constructor being called, so it will always be non nil.

It may be possible to do this:

constructor TFred.Create;
begin
  Self.AClassProcedureInThisClass;
end;

Cheers,

Raymond.
0
 
MadshiCommented:
As far as I understand this constructor logic, the order of all is this:

(1) You call "fredVar := TFred.Create"
(2) Delphi allocates memory for the new instance
(3) Delphi sets all variables to 0/nil
(4) Delphi sets "self" to the newly created instance
(5) Delphi calls the highest constructor (here TFred.Create)
(6) Now you *can* (and should) call the inherited constructors which initialize further variables

So "self" is valid from the very beginning (even before the first code line of TFred is being executed) to the very end.

Regards, Madshi.
0
 
LischkeCommented:
.. even better: Self is not a variable a such but an implicit operand. It is automatically loaded into EAX (from the reference where the class instance is stored in or a stack based variable in case of a not define reference holder like in the example: TBitmap.Create.Free) on call of a method of the specified class. The actual code of all methods must be already in memory and is executed depending on the implicit Self parameter, which is nothing else than a pointer to a piece of memory containing variables and the method tables for dynamic and virtual methods (DMT, VMT). These tables are filled by the compiler with all those method addresses which are overriden (or newly virtualized) in the particular class.

Now to Madshi's assertion that Self can never be nil. This is not true. In particular when it comes to class functions then Self is indeed nil. Another example is this:

var
  MyClass: TMyClass;

begin
  MyClass := nil;
  MyClass.AddItem(NewItem);
end;

Let's assume there's a method AddItem in TMyClass then the second call will enter this method with Self = nil.

This example answers andru's question again. By calling grounds.make without instantiating the class first a wrong call is made with usual desastrous results like overwritten method tables or other memory.

So to make a long story short. Call:

  if Grounds = nil then Grounds := TGrounds.Create;
  Grounds.Make;

Ciao, Mike
0
 
MadshiCommented:
Thanx for the deep look into this stuff, Mike...   :-)

Hmm... With "self is never nil" I didn't think about class methods or methods being called from a "nil" object variable, I thought about the normal "life cycle" of an object from the constructor to the destructor. If we don't write buggy code (and if we forget class methods for a while) I think we still can say: "self is (normally) never nil". So no need to ask "if self = nil then" in the constructor...

Regards, Madshi.
0
 
ptmcompCommented:
[snip]

unit test;

interface

type
  Tgrounds=class(TForm)
    field_1:longint;
    field_2:longint;
    constructor make;
  end;

var
  grounds:Tgrounds;

implementation

constructor tgrounds.make;
begin
  if grounds=nil then  // grounds is diffrent from TGrounds!
    inherited;
  field_1:=1;
  field_2:=2;
end;

end.

[snip]

unit test2;

interface

uses
  test;

implementation

{const
  grounds:tgrounds=nil; } // don't redefine this! You have to use the grounds varible of unit1.

begin
  grounds:=TGrounds.make;  { A Constructor is a method of the class, not of an object instance! The return value is the new instance. Assign it to a varible of the same type (or lower e.g. TObject). }
end.
 
[snip]

Regards, ptm.

PS: I didn't try if the code really works like this...
0
 
ptmcompCommented:
What I didn't already mention: In the constructor self is not the instance of the class, self is the class itself. In methods self is the object instance and self.class is the class of the object.
0
 
andruAuthor Commented:
well, ok thanks folks for the insight. but i don't think you see the reason for my madness :)

when grounds.make, or any other calls to make by instances of tground are made, I want 'make' to be able to know if the self object is instantiated or not. so therefore it would know whether to call the inherited constructor.

also, the reason i call my constructor make is i get the warning that tcustomform.create is blotted out.

andrew
0
 
ptmcompCommented:
What about using a class method instead of a constructor? (That will work with instances and with the class itself.)
0
 
andruAuthor Commented:
yes but how can that method create an instance of the non-existant class? cos if the rest of the code is executed, an gpf is triggered, cos the fields arnt allocated.

andrew
0
 
MadshiCommented:
It must look like this:

grounds := TGrounds.Make;
0
 
ptmcompCommented:
What's about a function returning an instance of TGrounds?

function GetGrounds: TGrounds;
begin
  if not (Grounds is TGrounds) then
    Grounds:= TGrounds.Create;
  Result:=Grounds;
end;
0
 
andruAuthor Commented:
function GetGrounds: TGrounds;
begin
  if not (Grounds is TGrounds) then
    Grounds:= TGrounds.Create;
  Result:=Grounds;
end;

this means i have to pass grounds as a parameter to the constructor make. i want make to know if the class needs to be created, based on self or whatever.

andrew
0
 
rwilson032697Commented:
I think I get it - you want a singleton object! IE: a class for which there is only one object. Is that right?

If so your code could look like this:

unit test;

interface

type
  Tgrounds=class(TForm)
    field_1:longint;
    field_2:longint;
    constructor Create;
    class Function Make : TGrounds
  end;

implementation

var
  grounds:Tgrounds = Nil;

class function tgrounds.make : TGrounds;
begin
  if grounds=nil then
    grounds := TGrounds.Create;
  Result := Grounds;
end;

constructor TGrounds.Create;
begin
  inherited;
  field_1:=1;
  field_2:=2;
end;

Cheers,

Raymond.
0
 
andruAuthor Commented:
no not one object. i want to prevent loads of instances of

  if grounds=nil then
    grounds:=tgrounds.make;
  grounds.initialise;

and have just

  grounds.make;

and allow the make constructor to decide if the object needs creating.

andrew
0
 
LischkeCommented:
Andrew,

the constructor is the wrong place to decide about the instance of a class. This decision must be made in higher levels. Look at the Printers.pas unit. There's a function just called Printer. This returns the default printer object and creates it when it is not yet instantiated:

function Printer: TPrinter;
begin
  if FPrinter = nil then FPrinter := TPrinter.Create;
  Result := FPrinter;
end;

This is the way how it should happen. The constructor is the wrong level and should only decide about its own data or that of its chilren (if any). That's called structured programming.

Ciao, Mike
0
 
andruAuthor Commented:
well folks, lets update the revised code

[snip]

unit test;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs;

type
  tgrounds=class(tform)
    field_1:longint;
    field_2:longint;

    constructor _create;
    procedure make;
  end;

var
  grounds:tgrounds;

implementation

constructor tgrounds._create;
begin
  inherited;
end;

procedure tgrounds.make;
begin
  if not (self is TGrounds) then
    self:=tgrounds._create;
  field_1:=1;
  field_2:=2;
end;

{$R *.DFM}

end.

[snip]

the problem is the 'self' lines don't get compiled. now guys, the point of all this is, the object being created/initialised is not always the same object, hence the use of self.

what confuses me, is in the original code, self shows up in a watch as nil, but the inherited line is ignored. i suppose i can't do what i want, cos how can a vmt exist for ground when it is assigned to nil.

andrew
0
 
LischkeCommented:
Andrew, it seems you are not listening. I'm getting out of this discussion...
0
 
andruAuthor Commented:
i changed the line

if not (self is TGrounds) then
  self:=tgrounds._create;

as i originally coded to

if self=nil then
  self:=tgrounds._create;

and it all works great, so the fix i think was to do the test outside the constructor just as lischke suggested, so the points go to lischke.

thanks for all the help guys.

andrew
0
All Courses

From novice to tech pro — start learning today.