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?
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.
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!
Related
I have a Base Class that needs a Prameter like Filename. This Parameter is needed for the Create Call of the TMemIniFile Class.
Whats the correct Way to call the Class function of the current Classtype inside the Base Class Constructor?
TBaseClassClass = Class of TBaseClass;
TBaseClass = class(TMemIniFile)
protected
class function FileName: string; virtual; abstract;
public
// Current Solution
constructor Create(ClassToCreate: TBaseClassClass); reintroduce;
// wanted Solution
constructor Create; reintroduce;
end;
TImplementation = Class(TBaseClass)
protected
class function FileName: string; override;
end;
// Current Solution
constructor TBaseClass.Create(ClassToCreate: TBaseClassClass);
begin
inherited Create(ClassToCreate.FileName, Encoding.UTF8);
end;
// Wanted Solution
constructor TBaseClass.Create;
begin
inherited Create(FileName, Encoding.UTF8); // This call to FileName has a Abstract Error because its calling Filename from TBaseClass. But how to solve this elegant?
end;
//Trying to Clarify: Usage:
myWorkingIniFile: TImplementation.Create; //Filename is defined in Class and the FileName value is used in the Constructor ...
Can i somehow "dynamically" Typecast inside the Constructor? I have seen the use of RTTI with a call of the function name. But i think there should be another Way.
Or am i doing it fundamentally wrong?
You should never create an instance of a class containing abstract methods. In your example, you should only create instances of TImplementation, never TBaseClass. And if you want the class to be dynamic :
procedure Foo;
var
vMyClass : TBaseClassClass;
begin
vMyClass := TImplementation; //or vMyClass := GetSomeClass();
FMyField := vMyClass.Create;
end;
In this case, you would normally want the constructor of TBaseClass to be virtual.
if you are calling TBaseClass.Create(TImplementation), you are right, that's fundamentally wrong
Oh yes thanks for the Comments. (German saying: i did not see the forest for all the trees; something like i did not see the obvious) I came up with something like this:
TBaseClass = class(TMemIniFile)
protected
class function FileName: string; virtual; abstract;
end;
TImplementation = Class(TBaseClass)
protected
class function FileName: string; override;
public
constructor Create; reintroduce;
end;
constructor TImplementation.Create;
begin
inherited Create(Filename, Encoding);
end;
Additional i now use AfterConstruction for all the other Settings and Setup. no need to use class functions.
I'm still a bit fuzzy with generics in Delphi, but have been using TObjectList<> quite widely. Now I have a situation where I have a base class with such a private field, but needs to be created for an arbitrary class, also inherited from another base.
To clarify, I have two base classes:
type
TItem = class;
TItems = class;
TItemClass = class of TItem;
TItem = class(TPersistent)
private
FSomeStuffForAllIneritedClasses: TSomeStuff;
end;
TItems = class(TPersistent)
private
FItems: TObjectList<TItem>;
FItemClass: TItemClass;
public
constructor Create(AItemClass: TItemClass);
destructor Destroy; override;
function Add: TItem;
...
end;
This pair of classes is then further inherited into more specific classes. I'd like the object list to be shared for all of them, while each holds actually a different type internally.
type
TSomeItem = class(TItem)
private
FSomeOtherStuff: TSomeOtherStuff;
...
end;
TSomeItems = class(TItems)
public
function Add: TSomeItem; //Calls inherited, similar to a TCollection
procedure DoSomethingOnlyThisClassShouldDo;
...
end;
Now the problem is when it comes to creating the actual object list. I'm trying to do it like this:
constructor TItems.Create(AItemClass: TItemClass);
begin
inherited Create;
FItemClass:= AItemClass;
FItems:= TObjectList<AItemClass>.Create(True);
end;
However, the code insight complains about this:
Undeclared Identifier AItemClass
Even more, the compiler has yet a different complaint:
Undeclared Identifier TObjectList
Where, I do in fact have System.Generics.Collections used in this unit.
What am I doing wrong here, and how should I do this instead?
Make TItems generic:
TItems<T: TItem, constructor> = class(TPersistent)
private
FItems: TObjectList<T>;
public
constructor Create;
destructor Destroy; override;
function Add: T;
...
end;
constructor TItems.Create;
begin
inherited Create;
FItems:= TObjectList<T>.Create(True);
end;
function TItems<T>.Add: T;
begin
Result := T.Create;
FItems.Add(Result);
end;
If you inherit, simply put the correct generic parameter:
TSomeItems = class(TItems<TSomeItem>)
public
procedure DoSomethingOnlyThisClassShouldDo;
...
end;
The TObjectList is not meant to be used in that manner. The fact that it was originally defined as TObjectList<TItem> means that it will expect you to create it this way as well. It needs to be defined with the precise class you intend to create it as.
Instead, just create it with TItem, and then whenever you create a new item which is supposed to be added to this list, then you create it using the class type. Any time you need to access the items in this list, just cast them on the fly.
For example...
Result:= FItemClass.Create;
FItems.Add(Result);
...can be the contents of your Add function.
Here is the object:
TCell = class(TPersistent)
private
FAlignmentInCell :byte;
public
constructor Create; virtual;
published
property AlignmentInCell:byte read FAlignmentInCell write FAlignmentInCell;
end;
this is its constructor:
constructor TCell.Create;
begin
inherited;
FAlignmentInCell:=5;
end;
Here is a function, which dynamically creates any object derived form TPersistent (parameter is class name provided as a string)
function CreateObjectFromClassName(AClassName:string):TPersistent;
var DynamicObject:TPersistent;
TempObject:TPersistent;
DynamicPersistent:TPersistent;
DynamicComponent:TComponent;
PersistentClass:TPersistentclass;
ComponentClass:TComponentClass;
begin
PersistentClass:=TPersistentclass(FindClass(AClassName));
TempObject:=PersistentClass.Create;
if TempObject is TComponent then
begin
ComponentClass:=TComponentClass(FindClass(AClassName));
DynamicObject:=ComponentClass.Create(nil);
end;
if not (TempObject is TComponent) then
begin
DynamicObject:=PersistentClass.Create; // object is really TCell, but appropriate constructor seems to be not called.
end;
result:=DynamicObject;
end;
My idea is to create new Cell (TCell) like this:
procedure TForm1.btn1Click(Sender: TObject);
var p:TPersistent;
begin
p := CreateObjectFromClassName('TCell');
ShowMessage(IntToStr(TCell(p).AlignmentInCell)); // it is 0. (Why?)
end;
When I want to check AlignmentInCell property I get 0, but I expected 5. Why? Is there way to fix it?
This is similar to a recent question.
You use TPersistentClass. But TPersistent does not have a virtual constructor, so the normal constructor for TPersistent is called, which is the constructor it inherits from TObject.
If you want to call the virtual constructor, you will have to declare a
type
TCellClass = class of TCell;
Now you can modify CreateObjectFromClassName to use this metaclass instead of TPersistenClass, and then the actual constructor will be called.
Also, TempObject is never freed. And instead of is, I would rather use InheritsFrom.
I did not test the following, but it should work:
function CreateObjectFromClassName(const AClassName: string; AOwner: TComponent): TPersistent;
var
PersistentClass: TPersistentclass;
begin
PersistentClass := FindClass(AClassName);
if PersistentClass.InheritsFrom(TComponent) then
Result := TComponentClass(PersistentClass).Create(AOwner)
else if PersistentClass.InheritsFrom(TCell) then
Result := TCellClass(PersistentClass).Create
else
Result := PersistentClass.Create;
end;
The compiler can't know for sure what value your variable of type TPersistentClass will hold at run time. So he assumes that it is exactly that: a TPersistentClass.
TPersistentClass is defined as a class of TPersistent. TPersistent has no virtual constructor, the compiler will therefore not include a call to dynamically look up the address of the constructor in the VMT of the actual class, but a 'hard-coded' call to the only matching constructor TPersistent has: the one it inherits from its base class TObject.
It might be a decision with reasons I don't know, but if you had chosen to define TCell as following
TCell = class(TComponent)
private
FAlignmentInCell: byte;
public
constructor Create(AOwner: TComponent); override;
published
property AlignmentInCell:byte read FAlignmentInCell write FAlignmentInCell;
end;
you wouldn't need TempObject and all the decision making in your CreateObjectFromClassName function (and the possible leaks as pointed out by others):
function CreateObjectFromClassName(AClassName:string): TComponent;
var
ComponentClass:TComponentClass;
begin
ComponentClass:=TComponentClass(FindClass(AClassName));
Result := ComponentClass.Create(nil);
end;
And make sure to manage the Results life-time as it has no Owner.
I'm trying to create what I understand to be a Class Factory in Delphi 2007. I want to pass a derived class type into a function and have it construct an object of that class type.
I've found some good references, such as How can I create an Delphi object from a class reference and ensure constructor execution?, but I still can't get it to work quite right. In the test below, I can't get it to call the derived constructor, even though the debugger tells me that oClass is TMyDerived.
I think I'm confused about something fundamental here and could use some explanation. Thanks.
program ClassFactoryTest;
{$APPTYPE CONSOLE}
uses
SysUtils;
// BASE CLASS
type
TMyBase = class(TObject)
bBaseFlag : boolean;
constructor Create; virtual;
end;
TMyBaseClass = class of TMyBase;
constructor TMyBase.Create;
begin
bBaseFlag := false;
end;
// DERIVED CLASS
type
TMyDerived = class(TMyBase)
bDerivedFlag : boolean;
constructor Create;
end;
constructor TMyDerived.Create;
begin
inherited;
bDerivedFlag := false;
end;
var
oClass: TMyBaseClass;
oBaseInstance, oDerivedInstance: TMyBase;
begin
oClass := TMyBase;
oBaseInstance := oClass.Create;
oClass := TMyDerived;
oDerivedInstance := oClass.Create; // <-- Still calling Base Class constructor
end.
You neglected to specify override on the derived class constructor. (I would have expected a warning from the compiler about hiding the base-class method.) Add that, and you should see TMyDerived.Create called.
TMyDerived = class(TMyBase)
bDerivedFlag : boolean;
constructor Create; override;
end;
An alternative, since your constructors take no parameters, is to forgo virtual constructors and just override AfterConstruction.
I have a class hierarchy, this one:
type
TMatrix = class
protected
//...
public
constructor Create(Rows, Cols: Byte);
//...
type
TMinMatrix = class(TMatrix)
private
procedure Allocate;
procedure DeAllocate;
public
constructor Create(Rows, Cols: Byte);
constructor CreateCopy(var that: TMinMatrix);
destructor Destroy;
end;
So as you see, both derived and base class constructors have the same parameter list.
I explicitly call base class constructor from derived one:
constructor TMinMatrix.Create(Rows, Cols: Byte);
begin
inherited;
//...
end;
Is it necessary to explicitly call base class constructor in Delphi? May be I need to put overload or override to clear what I intend to do? I know how to do it in C++ - you need explicit call of a base class constructor only if you want to pass some parameters to it - but I haven`t much experience in Delphi programming.
As far as I know, there are two separate issues here:
Making sure the child class' constructor calls the base class' constructor
You'll have to explicitly call the base class' constructor:
constructor TMinMatrix.Create(Rows, Cols: Byte);
begin
inherited;
//...
end;
Making sure the child class' constructor overrides the base class' constructor
You'll also have to make the child class' constructor override, and the base class' constructor virtual, to make sure the compiler sees the relation between the two. If you don't do that, the compiler will probably warn you that TMinMatrix's constructor is "hiding" TMatrix's constructor. So, the correct code would be:
type
TMatrix = class
protected
//...
public
constructor Create(Rows, Cols: Byte); virtual; // <-- Added "virtual" here
//...
type
TMinMatrix = class(TMatrix)
private
//...
public
constructor Create(Rows, Cols: Byte); override; // <-- Added "override" here
constructor CreateCopy(var that: TMinMatrix);
destructor Destroy; override; // <-- Also make the destructor "override"!
end;
Note that you should also make your destructor override.
Introducing a constructor with different parameters
Note that you can only override a constructor with the same parameter list. If a child class needs a constructor with different parameters, and you want to prevent the base class' constructors from being called directly, you should write:
type
TMyMatrix = class(TMatrix)
//...
public
constructor Create(Rows, Cols, InitialValue: Byte); reintroduce; virtual;
//...
end
implementation
constructor TMyMatrix.Create(Rows, Cols, InitialValue: Byte);
begin
inherited Create(Rows, Cols); // <-- Explicitly give parameters here
//...
end;
I hope this makes things more clear... Good luck!
Overload, tells the compiler that a method has the same name with different parameters.
Override, tells the compiler that a method overrides it virtual or dynamic declared in the base class.
Reintroduce, will hide the virtual or dynamic method declared in the base class.
Theses definitions are from Ray Lischner's book {Delphi in a nutshell}
type
TFirst = class
private
FValue: string;
FNumber: Integer;
public
constructor Create(AValue: string; ANumber: integer);
property MyValue: string read FValue write FValue;
property MyNumber: Integer read Fnumber write FNumber;
end;
TSecond = class(TFirst)
public
constructor Create(AValue: string; ANumber: Integer);
end;
constructor TFirst.Create(AValue: string; ANumber: integer);
begin
MyValue := AValue;
MyNumber := ANumber;
end;
{ TSecond }
constructor TSecond.Create(AValue: string; ANumber: Integer);
begin
inherited;
end;
The TSecond as it is declared will call the create of the TFirst, without the inherited, the TSecond members stay empty.
You need to call the inherited method explicitly; Delphi won't do it for you. This is by design, because there are cases where you're working with virtual methods, for example, when you do not want to invoke the inherited behavior.
Also, as a matter of personal preference, I like to write out the inherited call completely. (inherited Create(Rows, Cols); as opposed to just inherited; for one simple reason: it makes it that much easier to traverse the code. If you've got a method call written out, you can control-click it and get to the ancestor method.
You need overload for both constructors if they have the same name.
type
TMatrix = class
protected
//...
public
constructor Create(Rows, Cols: Byte);
//...
type
TMinMatrix = class(TMatrix)
public
constructor Create(Rows, Cols: Byte); overload;
constructor Create(var that: TMinMatrix); overload;
end;
It is good practice to call the inherited constructor.
constructor TMinMatrix.Create(Rows, Cols: Byte);
begin
inherited Create(Rows, Cols); // Need to call the full name if the parameters are changed.
//...
end;