7
votes

This is probably a simple question, but i would like to know how to ensure a class' constructor is called.

If i have the following code:

type TMyObject = class(TObject)
  public
     constructor Create;override;
end;

implementation 

constructor TMyObject.Create;override;
begin
  inherited;
  //do other instantiation
end;

Delphi does not allow this - 'Cannot override a static method'.

What i would like to do is ensure that the object is created using my custom Create constructor AND prohibiting calling the ancestors Create constructor.

My current solution to the problem is to define a uniquely signatured Create constructor like so:

constructor Create(aName : String);overload;

but the programmer could potentially call the ancestors Create() method.

2
Thanks for the answers, is there any reason that Create is a static method, yet Destroy is virtual? And what is the preffered way - use AfterConstruction, or Create?Simon
You might want to ask another question about that, because it's kind of "too much" to answer in comments...Cosmin Prund
I'll try anyway - amongst other things it is much more likely to call a destructor on an arbitrary class (e.g. TObjectList with OwnsObject := True) than creating an object with an arbitrary class.Gerry Coll
Destructors don't need parameter lists and so there can be a single destructor declared in TObject that is overridden by all subclasses. For constructors you often have parameters and you can't override a method and change its parameters. Note that TComponent.Create is vitual because the form property streaming mechanism uses that capability.David Heffernan
The syntax of Delphi constructors means that you don't ever call the wrong constructor. In this question you are worrying about a problem that doesn't arise in practice.David Heffernan

2 Answers

14
votes

You simply re-introduce a constructor with the ancestor's name. Once you do that, there's no way for the user to create a TMyObject calling the constructor introduced in TObject. If you use code like this:

TMyObject = class
public
  constructor Create;
end;

constructor TMyObject.Create;
begin
  // I am not calling the inherited constructor because
  // I do not want to.
end;

You don't use the override modifier on TMyObject.Create because the ancestor's constructor is not virtual.

Using this scheme it is impossible for the user to create your TMyObject using a constructor introduced in an ancestor. In this case, the ancestor is TObject, and the only constructor it has is TObject.Create. If the user writes this code:

X := TMyObject.Create;

it's quite obvious, TMyObject's constructor would be called, not the one introduced in TObject.


If you're afraid users would jump through hoops in order to create your class using ancestor's constructor, you can do your stuff from the AfterConstruction method. That's a virtual method, so it gets called even if your object is created using a class reference of an ancestor's type:

TMyObject = class
public
  procedure AfterConstruction;override;
end;
6
votes

TObject.Create isn't a virtual constructor, hence the error.

The only ways "other programmers" could call the ancestral Create method are by deliberately jumping through some hoops to do so, e.g.

var
  ClassRef: TClass;
  Obj : TObject;
begin
  ClassRef := TMyObject;
  Obj := ClassRef.Create;
end;

as the newly introduced constructor will hide the non-virtual TObject.Create

What you want is probably more like:

type TMyObject = class(TObject)
  public
     constructor Create;virtual;
end;

implementation 

constructor TMyObject.Create;
begin
  inherited;
  //do other instantiation
end;