Delphi constructor and class constructor
Asked Answered
B

3

17

I have a doubt that I cannot solve. I have read the documentation at embarcadero for class constructors but I cannot understand the meaning of that. In other words, what is the usage difference between a constructor and a class constructor? I did this:

type
 TGeneric<T> = class
  private
   FValue: T;
   aboutString: string;
   procedure setValue(const a: T);
  public
   property V: T read FValue write setValue;
   property about: string read aboutString;
   constructor Create;
   destructor Destroy; override;
 end;

implementation

{ TGeneric<T> }

constructor TGeneric<T>.Create;
begin
  inherited;
  aboutString := 'test';
end;

Instead this code is not working properly:

type
 TGeneric<T> = class
  private
   FValue: T;
   aboutString: string;
   procedure setValue(const a: T);
  public
   property V: T read FValue write setValue;
   property about: string read aboutString;
   class constructor Create;
   destructor Destroy; override;
 end;

implementation

{ TGeneric<T> }

class constructor TGeneric<T>.Create;
begin
  inherited;
  aboutString := 'test';
end;

I guess that the answer is in this line of the documentation:

Normally, class constructors are used to initialize the static fields of the class or to perform a type of initialization, which is required before the class or any class instance can function properly.

Tell me if I am correct:

  • Constructor: I can use the inherited Create;, initialize variables and so on.
  • Class constructor: I can use this when I have to create an object in my class immediately?

For example look at below:

type
   TBox = class
   private
     class var FList: TList<Integer>;
     class constructor Create;
   end;

 implementation

 class constructor TBox.Create;
 begin
   { Initialize the static FList member }
   FList := TList<Integer>.Create();
 end;

 end.

Here I am going to create the object immediately when I call TBox.Create in the main form?

Beekeeping answered 13/9, 2016 at 13:54 Comment(3)
Think of class constructors as a way to automatically initiate your class type. They are automatically called at startup and you should never call them by your code. In other words, class constructors operates on the type, while normal constructors operates on your variables.Narrate
So I can avoid that and only care about the classic constructor?Beekeeping
That depends on what you want to do. A class var is a sort of global variable for that type and can be useful in some cases.Narrate
I
30
  • A class constructor executes exactly once, when the unit in which it is declared is initialized. A class constructor is a static class method, and so Self is not defined.
  • A constructor executes when explicitly called and has the job of initializing an instance of a class.

In the wild, class constructors are rare, constructors are as common as muck. Quite likely you have no immediate need for class constructors so feel free to ignore them for now. Concentrate on understanding how to write and use constructors.

If in the future you ever need to initialize variables owned by the class (as opposed to owned by an instance) then you might find yourself wanting to use a class constructor. Until that point in time, do feel free to ignore them.

Induce answered 13/9, 2016 at 14:44 Comment(6)
Note that a class constructor only executes if the class is used. So unlike a normal initialization section of a unit (which always executes), it only executes if the class is used somewhere. The same is true for a class destructor.Undercarriage
An initialization section executes if and only if the unit is referenced. Likewise a class constructor. Only called if the class is referenced.Induce
Only if the class is used. You can declare a variable of the class and yet it won't run. Init sections always run, once the unit is included.Undercarriage
@RudyVelthuis That's true. I found it hard to work out what "class is used" actually means though. For instance, this is enough to get the class' class constructor to run on the Win32 compiler from XE7: var obj: TMyClass; begin obj := nil; end. Induce
@David In Delphi 10.2 class constructor doesn't have "Self" at all (unlike other class methods). Compiler reports "undeclared identifier".Celestinacelestine
@AndreiGalatyn Thank you. My answer was wrong. Class constructors are static class methods and so do not have Self at all.Induce
H
1

In addition to my pre-posters, it should be mentioned that in your example, the class constructor of a generic class is being called each time you instantiate (reference) a genric specialization:

TGenClass<T: class> = class
public
  class constructor Create;
  class destructor Destroy;
end;

class constructor TGenClass<T>.Create;
begin
  Writeln('TGenClass<', GetTypeName(TypeInfo(T)), '> created');
end;

class constructor TGenClass<T>.Create;
begin
  Writeln('TGenClass<', GetTypeName(TypeInfo(T)), '> destroyed');
end;

initialization
  RegisterClass(TGenClass<String>);
  RegisterClass(TGenClass<Integer>);

The output will be:

TGenClass<String> created
TGenClass<Integer> created
TGenClass<String> destroyed
TGenClass<Integer> destroyed

Note however, that the class construction/destruction order does not necessarily have to match the declaration or reference order at all!

Horologium answered 22/11, 2022 at 15:3 Comment(0)
K
0

Quoting from Delphi 12 Athens documentation on this. It's not changed for years, having checked as far back as Berlin.

Class Constructors

A class constructor is a special class method that is not accessible to developers. Calls to class constructors are inserted automatically by the compiler into the initialization section of the unit where the class is defined. Normally, class constructors are used to initialize the static fields of the class or to perform a type of initialization, which is required before the class or any class instance can function properly. Even though the same result can be obtained by placing class initialization code into the initialization section, class constructors have the benefit of helping the compiler decide which classes should be included into the final binary file and which should be removed from it.

The next example shows the usual way of initializing class fields:

type
  TBox = class
  private
    class var FList: TList<Integer>;
  end;

implementation

initialization
  { Initialize the static FList member }
  TBox.FList := TList<Integer>.Create();

end.

This method has a big disadvantage: even though an application can include the unit in which TBox is declared, it may never actually use the TBox class. In the current example, the TBox class is included into the resulting binary, because it is referenced in the initialization section. To alleviate this problem, consider using class constructors:

type
  TBox = class
  private
    class var FList: TList<Integer>;
    class constructor Create;
  end;

implementation

class constructor TBox.Create;
begin
  { Initialize the static FList member }
  FList := TList<Integer>.Create();
end;

end.

In this case, the compiler checks whether TBox is actually used anywhere in the application, and if it is used, a call to the class constructor is added automatically to the initialization section of the unit.

Note: Even though the compiler takes care of ordering the initialization of classes, in some complex scenarios, ordering may become random. This happens when the class constructor of a class depends on the state of another class that, in turn, depends on the first class.

Note: The class constructor for a generic class or record may execute multiple times. The exact number of times the class constructor is executed in this case depends on the number of specialized versions of the generic type. For example, the class constructor for a specialized TList<String> class may execute multiple times in the same application.

Class Destructors

Class destructors are the opposite of class constructors in that they perform the finalization of the class. Class destructors come with the same advantages as class constructors, except for finalization purposes.

The following example builds on the example shown in class constructors and introduces the finalization routine:

type
  TBox = class
  private
    class var FList: TList<Integer>;
    class constructor Create;
    class destructor Destroy;
  end;

implementation

class constructor TBox.Create;
begin
  { Initialize the static FList member }
  FList := TList<Integer>.Create();
end;

class destructor TBox.Destroy;
begin
  { Finalize the static FList member }
  FList.Free;
end;

end.

Note: The class destructor for a generic class or record may execute multiple times. The exact number of times the class destructor is executed in this case depends on the number of specialized versions of the generic type. For example, the class destructor for a specialized TList<String> class may execute multiple times in the same application.

Kauri answered 15/2 at 14:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.