Use virtual constructor to reset to initial state - delphi

i do not have any experience with virtual constructors which are available in Delphi. I consider to use virtual ctors in a class hierachy to reset the instance to an initial state like this:
A = class
end;
B = class(A)
end;
C = class(B)
end;
FooA = class
a_ : A;
constructor Create(inst : A); overload;
constructor Create; overload; virtual; abstract;
destructor Destroy; override;
function Bar : A;
end;
FooB = class(FooA)
b_ : B;
constructor Create; override;
constructor Create(inst : B); overload;
end;
FooC = class(FooB)
// ...
end;
{ FooA }
constructor FooA.Create(inst: A);
begin
inherited Create;
a_ := inst;
end;
destructor FooA.Destroy;
begin
FreeAndNil(a_);
inherited;
end;
function FooA.Bar : A;
begin
Result := a_;
a_ := nil;
// here comes the magic
Self.Create;
end;
{ FooB }
constructor FooB.Create;
begin
b_ := B.Create;
inherited Create(b_);
end;
constructor FooB.Create(inst: B);
begin
inherited Create(inst);
b_ := inst;
end;
{ FooC } // ...
var
fc : FooA;
baz : A;
begin
fc := FooC.Create;
baz := fc.Bar;
WriteLn(baz.ClassName);
FreeAndNil(baz);
FreeAndNil(fc);
ReadLn;
end.
Are there any problems/pitfalls in this design? The simple example works like a charm but i feel a little bit uneasy calling constructors (which do not construct anything) like this.
Edit:
I decided to move the initialization to a method in protected area with a meaningful name, what makes me feel better ;-)
FooA = class
strict private
a_ : A;
strict protected
procedure SetInst; overload; virtual; abstract;
procedure SetInst(i : A); overload;
public
constructor Create;
destructor Destroy; override;
function Foo : A;
end;

Very few classes are written to support the use of constructors as re-initializers. They usually assume that any dynamically allocated memory has not already been allocated. If you're in control of all the classes you're using, then go ahead and carefully use constructors as re-initializers.
Even if you're in control, I'd still advise against it. It's not idiomatic Delphi; anyone else reading your code (perhaps even you, a few weeks or months from now) will be confused — at least at first — by your non-standard use of constructors. It's not worth the trouble. If calling the Bar function is supposed to release ownership of the A object and create a new instance, then write functions with names that make that clear.

Rob's right about this being really weird-looking code that's likely to confuse people, and moving your code to an initialization routine is a good idea. In case you were wondering, the main purpose of virtual constructors is for something completely different: to more easily support "factory" style object creation.
Some outside source provides some data that can identify any descendant of a base class, and the factory uses a class reference and calls a virtual constructor defined in the base class on it. That way you end up with an instance of the descendant class without having to hard-code knowledge of the descendant class into the factory code.
If this sounds a bit strange, take a look at a DFM file. It's got a list of form objects that descend from TComponent, with their published properties. When the form reading code comes across an object statement, it reads the class name, looks it up in a table that maps class names to class references, and calls the virtual TComponent.Create on that class reference. This calls the virtual constructor for the actual class, and it ends up with an instance of that type of component, and starts to fill in its properties.

Related

How to make Delphi call correct constructor during dynamic creating?

I'm having problems with my Delphi 2006 seeming to call the incorrect constructor during dynamic creation.
I asked almost the exact same question 5 yrs ago (Why does Delphi call incorrect constructor during dynamic object creation?), and I have reviewed that. But that thread had issues of overriding virtual calls which I don't have now. I have also tried searching through StackOverflow for a matching question, but couldn't find an answer.
I am working with legacy code, so I didn't write much of this. (If you see comments below with '//kt' adding something, that is me).
The code has base class, TPCEItem as follow. Note that it does NOT have a constructor.
TPCEItem = class(TObject)
{base class for PCE items}
private
<irrelevent stuff>
public
<irrelevent stuff>
end;
Next, there is class type to use for passing a parameter (more below).
TPCEItemClass = class of TPCEItem;
Next I have a child class as follows. Note that it DOES have a contructor. The compiler will not allow me to add 'override' to this create method because the ancestor class where this is declared (TObject) does not define it as virtual.
TPCEProc = class(TPCEItem)
{class for procedures}
protected
<irrelevent stuff>
public
<irrelevent stuff>
constructor Create;
destructor Destroy; override;
end;
The code then has a function for copying data, which is a conglomeration of descendant types. Because this is older code, mosts of these lists are plain TLists or TStringLists, holding untyped pointers. Thus for each copy command a corresponding type is passed in for correct use.
procedure TPCEData.CopyPCEData(Dest: TPCEData);
begin
Dest.Clear;
<irrelevent stuff>
CopyPCEItems(FVisitTypesList, Dest.FVisitTypesList, TPCEProc); //kt added
CopyPCEItems(FDiagnoses, Dest.FDiagnoses, TPCEDiag);
CopyPCEItems(FProcedures, Dest.FProcedures, TPCEProc);
CopyPCEItems(FImmunizations, Dest.FImmunizations, TPCEImm);
CopyPCEItems(FSkinTests, Dest.FSkinTests, TPCESkin);
CopyPCEItems(FPatientEds, Dest.FPatientEds, TPCEPat);
CopyPCEItems(FHealthFactors, Dest.FHealthFactors, TPCEHealth);
CopyPCEItems(FExams, Dest.FExams, TPCEExams);
<irrelevent stuff>
end;
This CopyPCEItems is as follows:
procedure TPCEData.CopyPCEItems(Src: TList; Dest: TObject; ItemClass: TPCEItemClass);
var
AItem: TPCEItem;
i: Integer;
IsStrings: boolean;
Obj : TObject;
begin
if (Dest is TStrings) then begin
IsStrings := TRUE
end else if (Dest is TList) then begin
IsStrings := FALSE
end else begin
exit;
end;
for i := 0 to Src.Count - 1 do begin
Obj := TObject(Src[i]);
if(not TPCEItem(Src[i]).FDelete) then begin
AItem := ItemClass.Create; //<--- THE PROBLEMATIC LINE
if (Obj.ClassType = TPCEProc) and (ItemClass = TPCEProc) then begin //kt added if block and sub block below
TPCEProc(Obj).CopyProc(TPCEProc(AItem));
end else begin
AItem.Assign(TPCEItem(Src[i])); //kt <-- originally this line was by itself.
end;
if (IsStrings) then begin
TStrings(Dest).AddObject(AItem.ItemStr, AItem)
end else begin
TList(Dest).Add(AItem);
end;
end;
end;
end;
The problematic line is as below:
AItem := ItemClass.Create;
When I step through the code with the debugger, and stop on this line, an inspection of the variable ItemClass is as follows
ItemClass = TPCEProc
The problems is that the .Create is calling TObject.Create, not TPCEProc.Create, which doesn't give me an opportunity to instantiate some needed TStringLists, and later leads to access violation error.
Can anyone help me understand what is going on here? I have a suspicion that the problem is with this line:
TPCEItemClass = class of TPCEItem;
It is because this is of a class of an ancestor type (i.e. TPCEItem), that it doesn't properly carry the information for the child type (TPCEProc)?? But if this is true, then why does the debugger show that ItemClass = TPCEProc??
How can I effect a call to TPCEProc.Create?
I have been programming in Delphi for at least 30 yrs, and it frustrates me that I keep having problems with polymorphism. I have read about this repeatedly. But I keep hitting walls.
Thanks in advance.
When you are constructing objects through meta-class you need to mark its base class constructor as virtual, and if you need a constructor in any of the descendant classes they need to override that virtual constructor.
If the base class does not have a constructor, you will need to add empty one.
TPCEItem = class(TObject)
public
constructor Create; virtual;
end;
TPCEItemClass = class of TPCEItem;
TPCEProc = class(TPCEItem)
public
constructor Create; override;
destructor Destroy; override;
end;
constructor TPCEItem.Create;
begin
// if the descendant class is TObject
// or any other class that has empty constructor
// you can omit inherited call
inherited;
end;
You have already identified the problem - the base class TPCEItem does not define a virtual constructor, it just inherits a constructor from TObject, which is not virtual.
As such, you cannot create instances of any TPCEItem-derived classes by using your TPCEItemClass metaclass type. In order for a metaclass to invoke the correct derived class constructor, the base class being referred to MUST have a virtual constructor, eg:
TPCEItem = class(TObject)
...
public
constructor Create; virtual;
end;
TPCEProc = class(TPCEItem)
...
public
constructor Create; override;
...
end;
procedure TPCEData.CopyPCEItems(...; ItemClass: TPCEItemClass);
var
AItem: TPCEItem;
...
begin
...
AItem := ItemClass.Create; // <-- THIS WORKS NOW!
...
if (Obj is TPCEProc) then begin // <-- FYI: use 'is' rather than ClassType to handle descendants of TPCEProc...
TPCEProc(Obj).CopyProc(TPCEProc(AItem));
...
end;
Congratulations you have identified the problematic line
AItem := ItemClass.Create; //<--- THE PROBLEMATIC LINE
But what is wrong with this line? You are calling constructor method from existing class instance. You should not do this ever. You should only call constructor methods from specific class types not existing class instances.
So in order to fix your code change the mentioned line to
AItem := TPCEItem.Create;
You may be thinking of perhaps calling AItem := TPCEItemClass.Create; since above in your code you made next declaration
TPCEItemClass = class of TPCEItem;
This declaration does not meant that TPCEItemClass is the same type as TPCEItem but instead that both types have same type structure but they are in fact two distinct types.
By the way what is the purpose of ItemClass: TPCEItemClass parameter of your CopyPCEItems procedure if you are not even using it in your procedure but instead work with local variable AItem: TPCEItem all the time? Well at least in your shown code that is.

Delphi - Strange behavior with smart pointer constructors

I'm working on a project containing several packages. In one of my base packages I declare a smart pointer, like that (here is the complete code):
unit UTWSmartPointer;
interface
type
IWSmartPointer<T> = reference to function: T;
TWSmartPointer<T: class, constructor> = class(TInterfacedObject, IWSmartPointer<T>)
private
m_pInstance: T;
public
constructor Create; overload; virtual;
constructor Create(pInstance: T); overload; virtual;
destructor Destroy; override;
function Invoke: T; virtual;
end;
implementation
//---------------------------------------------------------------------------
constructor TWSmartPointer<T>.Create;
begin
inherited Create;
m_pInstance := T.Create;
end;
//---------------------------------------------------------------------------
constructor TWSmartPointer<T>.Create(pInstance: T);
begin
inherited Create;
m_pInstance := pInstance;
end;
//---------------------------------------------------------------------------
destructor TWSmartPointer<T>.Destroy;
begin
m_pInstance.Free;
m_pInstance := nil;
inherited Destroy;
end;
//---------------------------------------------------------------------------
function TWSmartPointer<T>.Invoke: T;
begin
Result := m_pInstance;
end;
//---------------------------------------------------------------------------
end.
Later in my project (and in another package), I use this smart pointer with a GDI+ object (a TGpGraphicsPath). I declare the graphic path like that:
...
pGraphicsPath: IWSmartPointer<TGpGraphicsPath>;
...
pGraphicsPath := TWSmartPointer<TGpGraphicsPath>.Create();
...
However, nothing is drawn on the screen when I execute the code. I get no error, no exception or access violation, just a blank page. But if I just change my code like that:
...
pGraphicsPath: IWSmartPointer<TGpGraphicsPath>;
...
pGraphicsPath := TWSmartPointer<TGpGraphicsPath>.Create(TGpGraphicsPath.Create);
...
then all become fine, and my path is painted exactly as expected. But I cannot figure out why the first constructor does not work as expected. Somebody can explain to me this strange behavior?
Regards
This is quite a complex trap that you have fallen into. When you write:
TGpGraphicsPath.Create
you might think that you are calling the parameterless constructor. But it is not so. You are in fact calling this constructor:
constructor Create(fillMode: TFillMode = FillModeAlternate); reintroduce; overload;
You supply no argument, so the default value is provided by the compiler.
In your smart pointer class you write:
T.Create
This really is calling the parameterless constructor. But that is the constructor defined by TObject. When that constructor is used, the TGPGraphicsPath instance is not properly initialised.
If you are going to use the constructor generic constraint, you must also ensure that you always use a class that can be properly constructed with a parameterless constructor. Unfortunately for you TGPGraphicsPath does not fit the bill. Indeed there are a preponderance of such classes.
There's really not a whole lot that you can do here to avoid explicitly calling the constructor. It's pretty much impossible for your smart pointer class to work out which constructor to call, for this particular class.
My advice would be to steer away from the constructor generic constraint and force the consumer of the smart pointer class to explicitly instantiate the instance.
This is quite a common issue – I answered a similar question here less than a week ago: Why does a deserialized TDictionary not work correctly?

Dealing with circular strong references in Delphi

I got two classes (in my example TObject1 and TObject2) which know each other via interfaces (IObject1, IObject2). As you probably know in Delphi this will lead to a memory leak as both reference counter will always stay above zero. The usual solution is declaring one reference as weak. This works in most cases because you usually know which one will be destroyed first or don't necessarily need the object behind the weak reference once it is destroyed.
This said I tried to solve the problem in a manner that both objects stay alive until both aren't referenced anymore: (Delphi 10.1 required as I use the [unsafe] attribute)
program Project14;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
IObject2 = interface;
IObject1 = interface
['{F68D7631-4838-4E15-871A-BD2EAF16CC49}']
function GetObject2: IObject2;
end;
IObject2 = interface
['{98EB60DA-646D-4ECF-B5A7-6A27B3106689}']
end;
TObject1 = class(TInterfacedObject, IObject1)
[unsafe] FObj2: IObject2;
constructor Create;
destructor Destroy; override;
function GetObject2: IObject2;
end;
TObject2 = class(TContainedObject, IObject2)
[unsafe] FObj1: IObject1;
constructor Create(aObj1: IObject1);
destructor Destroy; override;
end;
constructor TObject1.Create;
begin
FObj2 := TObject2.Create(Self);
end;
destructor TObject1.Destroy;
begin
TContainedObject(FObj2).Free;
inherited Destroy;
end;
function TObject1.GetObject2: IObject2;
begin
Result := FObj2;
end;
constructor TObject2.Create(aObj1: IObject1);
begin
inherited Create(aObj1);
FObj1 := aObj1;
end;
destructor TObject2.Destroy;
begin
inherited Destroy;
end;
function Test1: IObject1;
var
x: IObject2;
begin
Result := TObject1.Create;
x := Result.GetObject2;
end;
function Test2: IObject2;
var
x: IObject1;
begin
x := TObject1.Create;
Result := x.GetObject2;
end;
var
o1: IObject1;
o2: IObject2;
begin
try
o1 := Test1();
o2 := Test2();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
This does work as it is.. function Test1 and Test2 each create one instance of TObject1 and TObject2 referencing each other and all instances get destroyed once o1 and o2 go out of scope. The solution is based on TContainedObject which forwards the refcounting to the "controller" (TObject1 in this case).
Now I know this solution has flaws, and this is where my questions start:
"TContainedObject(FObj2).Free;" smells a bit, but I don't have a better solution as I need to use an interface to reference to TObject2 (the productive code contains a few inheritance on this end). Any ideas to clean it up?
you easily forget to declare all reference between the 2 classes as weak and ..
a similar problem starts to raise with more classes: Having TObject3 which is referenced by one and references the other: memory leak. I could handle it by letting it descent from TContainedObject too but with legacy code this might not be an easy task.
I have the feeling this solution can't be applied universally and hoping for one which can - or maybe an answer that will describe why it is so hard or even impossible to have an easy to use 100%-solution to manage such situations.
Imho it can't be to complicated to have a finite amount of object which destroy each other once they are not referenced from out of their domain without having to carefully think about every reference within this domain.
Don't use unsafe
[unsafe] should not be used in normal code.
It is really a hack to the used if you don't want the compiler to do reference counting on interfaces.
Use weak instead
If for some reason you must have circular references then use a [weak] attribute on one of the references and declare the other reference as usual.
In your example it would look like this:
TParent = class(TInterfacedObject, IParent)
FChild: IChild; //normal child
constructor Create;
function GetObject2: IChild;
end;
TChild = class(TContainedObject, IChild)
//reference from the child to the parent, always [weak] if circular.
[weak] FObj1: IParent;
constructor Create(const aObj1: IParent);
end;
Now there is no need to do anything special in the destructors, so these can be omitted.
The compiler tracks all weak references and sets them to nil when the reference count of the referenced interface reaches zero.
And all this is done in a thread-safe manner.
However the weak reference itself does not increase the reference count.
When to use unsafe
This is in contrast to the unsafe reference, where no tracking and no reference counting at all takes place.
You would use an [unsafe] reference on an interfaced type that is a singleton, or one that has disabled reference counting.
Here the ref count is fixed at -1 in any case, so the calling of addref and release is an unneeded overhead.
Putting the [unsafe] eliminates that silly overhead.
Unless your interfaces override _addref and _release do not use [unsafe].
Pre Berlin alternative
Pre Berlin there is no [weak] attribute outside the NexGen compilers.
If you are running Seattle, 2010 or anything in between the following code would do {almost} the same.
Although I'm unsure if this code might not fall victim to race conditions in multithreaded code.
If that's a concern for you feel free to raise a flag and I'll investigate.
TParent = class(TInterfacedObject, IParent)
FChild: IChild; //normal child
constructor Create;
function GetObject2: IChild;
end;
TChild = class(TContainedObject, IChild)
//reference from the child to the parent, always [weak] if circular.
FObj1: TParent; //not an interface will not get refcounted.
constructor Create(const aObj1: IParent);
destructor Destroy; override;
end;
constructor TChild.Create(const aObj1: IParent);
begin
inherited Create;
FObject1:= (aObj1 as TParent);
end;
destructor TParent.Destroy;
begin
if Assigned(FChild) then FChild.InvalidateYourPointersToParent(self);
inherited;
end;
This will also ensure the interfaces get properly disposed, however now TChild.FObject1 will not automatically get nilled. You might be able to put code in the destructor of the TParent to visit all its children and inform them as in the code shown.
If one of the participants in the circular reference can't inform its weakly linked counterparts then you'll need to setup some other mechanism to nil those weak references.
If you want to keep both objects alive or dead together, the surely they are one single object. OK, I get that both may be developed by different people, so then I would make them both members of one super-object that is reference counted, like this
type
TSuperobject = class( TInterfaceObject, IObject1, iObject2 )
private
fObject1 : TObject1;
fObject2 : TObject2;
public
constructor Create;
destructor Destroy;
function GetObject2: IObject2;
etc.
end;
etc.
The details should be obvious. Any reference to object1 or object2 must reference the owning object( superobject.object1 etc.), so object1 and object2 themselves do not need to be reference counted - i.e. they can be regular objects, not interfaced objects, but it actually doesn't matter if they are reference counted because the owner will always add 1 to the reference count (in that case you may not need the destructor in the superobject). If you are leaving object1 and object2 as referenced objects make their refence to each other both weak.
You are solving the wrong problem here.
Your actual problem is not in strong - weak references nor how your solution can be improved. Your problem is not in how to achieve, but in what you are achieving (want to achieve).
To directly address your questions first:
"TContainedObject(FObj2).Free;" smells a bit, but I don't have a better solution as I need to use an interface to reference to TObject2
(the productive code contains a few inheritance on this end). Any
ideas to clean it up?
You cannot do much here. You must call Free on FObj2 because TContainedObject is not managed class itself.
you easily forget to declare all reference between the 2 classes as weak and ..
You cannot do anything here either. It comes with the territory. If you want to use ARC you have to think about circular references.
a similar problem starts to raise with more classes: Having TObject3 which is referenced by one and references the other: memory
leak. I could handle it by letting it descent from TContainedObject
too but with legacy code this might not be an easy task.
You cannot do much here either. If your design is really what you want to have, then you will just have to deal with its complexities.
Now, back to why you are having problems in the first place.
What you want to achieve (and you have done so with your example code) is keeping whole object hierarchy alive by grabbing any of the object references inside that hierarchy.
To rephrase, you have Form and a Button on it and you want to keep Form alive is something holds a Button (because Button itself would not function). Then you want to add Edit to that Form and again keep everything alive if something grabs Edit.
You have few options here.
Keep this broken design and live with your solution because you have too much code involved and change would be painful. If you do that keep in mind that this is ultimately broken design and don't attempt to repeat it anywhere else.
If you have hierarchy where TObject1 is root class that holds all else, then refactor it and inherit TObject2 from TInterfacedObject to have its own reference counting and don't grab references to FObj2. Instead grab root TObject1 instance and pass that around, if you really need to.
This is variation of second approach. If TObject1 is not the root class then create additional wrapper class containing all instances you need and pass that one around.
Last two solutions are far from perfect and they don't deal with fact that you probably have classes that are doing too much or similar. But no matter how bad that code might be, it does not even come close to your current solution. And with time you can slowly change and improve those solutions much easier than with your current one.
It looks like you want both objects to share their reference count. You could do that by letting a third object (TPair) handle the reference counting. A nice way to accomplish this is by using the implements keyword. You can choose to keep this third object hidden, or to interact with that as well.
With the code below you can either create a TPairChildA, a TPairChildB or their 'parent' TPair. Any of them will create the others when needed and all created objects will be kept alive until none are referenced anymore. You can of course add interfaces like your IObject1 to the objects, but I kept them out for simplicity.
unit ObjectPair;
interface
type
TPairChildA = class;
TPairChildB = class;
TPair = class( TInterfacedObject )
protected
FChildA : TPairChildA;
FChildB : TPairChildB;
function GetChildA : TPairChildA;
function GetChildB : TPairChildB;
public
destructor Destroy; override;
property ChildA : TPairChildA read GetChildA;
property ChildB : TPairChildB read GetChildB;
end;
TPairChild = class( TObject , IInterface )
protected
FPair : TPair;
property Pair : TPair read FPair implements IInterface;
public
constructor Create( APair : TPair = nil ); virtual;
end;
TPairChildA = class( TPairChild )
protected
function GetSibling : TPairChildB;
public
constructor Create( APair : TPair = nil ); override;
property Sibling : TPairChildB read GetSibling;
end;
TPairChildB = class( TPairChild )
protected
function GetSibling : TPairChildA;
public
constructor Create( APair : TPair = nil ); override;
property Sibling : TPairChildA read GetSibling;
end;
implementation
//==============================================================================
// TPair
destructor TPair.Destroy;
begin
FChildA.Free;
FChildB.Free;
inherited;
end;
function TPair.GetChildA : TPairChildA;
begin
if FChildA = nil then
FChildA := TPairChildA.Create( Self );
Result := FChildA;
end;
function TPair.GetChildB : TPairChildB;
begin
if FChildB = nil then
FChildB := TPairChildB.Create( Self );
Result := FChildB;
end;
// END TPair
//==============================================================================
// TPairChild
constructor TPairChild.Create( APair : TPair = nil );
begin
if APair = nil then
FPair := TPair.Create
else
FPair := APair;
end;
// END TPairChild
//==============================================================================
// TPairChildA
constructor TPairChildA.Create( APair : TPair = nil );
begin
inherited;
FPair.FChildA := Self;
end;
function TPairChildA.GetSibling : TPairChildB;
begin
Result := FPair.ChildB;
end;
// END TPairChildA
//==============================================================================
// TPairChildB
constructor TPairChildB.Create( APair : TPair = nil );
begin
inherited;
FPair.FChildB := Self;
end;
function TPairChildB.GetSibling : TPairChildA;
begin
Result := FPair.ChildA;
end;
// END TPairChildB
//==============================================================================
end.
A usage example:
procedure TForm1.Button1Click( Sender : TObject );
var
objA : TPairChildA;
ifA , ifB : IInterface;
begin
objA := TPairChildA.Create;
ifA := objA;
ifB := objA.Sibling;
ifA := nil;
ifB := nil; // This frees all three objects.
end;

Is it possible to do a "super inherited constructor" invocation up a hiearchy of non-virtual constructors or methods?

Suppose in Delphi you have these classes:
type
TClass1 = class
public
constructor Create;
end;
TClass2 = class(TClass1)
public
constructor Create;
end;
TClass3 = class(TClass2)
public
constructor Create;
end;
Note that TClass1.Create is not virtual, and that TClass2 and TClass3 declare a constructor which is not virtual.
Suppose that I want to invoke TClass1's Create constructor-method, from within TClass3.Create, but not invoke the constructor-code in TClass2.Create? Is this possible within the language without recourse to RTTI?
I don't think there is such a syntax, but what I want is:
constructor TClass3.Create;
begin
super inherited Create; // Invoke TClass1.Create
end;
The closest I can get is this which compiles but just leaks an object, as it's doing a separate TClass1.Create construction.
constructor TClass3.Create;
begin
TClass1.Create; // returns new TClass1, discards and leaks it.
// other initialization here.
end;
It also seems to me that the code TClass1.Create invocation within TClass3.Create compiles, I cannot call it correct, it is wrong because it leaks an object. What is the correct right way to do it?
Update Note that David's answer works for a class hiearchy without virtual constructors, only, as I originally asked. His answer would not work in your code, if you had virtual constructors and TClass2 and TClass3 overrode them. If I had asked the above question with virtual constructors (or a virtual method that is not a constructor) the answer would be "you can't do it at all, except by really gross Virtual Method Table hacks". Also note that the linked "possible duplicate" is not a duplicate because the answer changes when you add/subtract virtual methods from the situation.
There is no syntactical support for skipping a layer of the inheritance hierarchy. The only way you can do what you want is like this:
TClass1(Self).Create;
A complete example program to demonstrate:
type
TClass1 = class
constructor Create;
end;
TClass2 = class(TClass1)
constructor Create;
end;
TClass3 = class(TClass2)
constructor Create;
end;
constructor TClass1.Create;
begin
Writeln('TClass1');
end;
constructor TClass2.Create;
begin
inherited;
Writeln('TClass2');
end;
constructor TClass3.Create;
begin
TClass1(Self).Create;
Writeln('TClass3');
end;
begin
TClass3.Create;
Readln;
end.
Output
TClass1
TClass3
While you should not do this you actually can achieve it with inline assembly code:
constructor TClass3.Create;
begin
asm
mov eax, Self
call TClass1.Create;
end;
Writeln('TClass3');
end;
But keep in mind that this is actually different from a theoretical super inherited (which would skip one inheritance level) while this just calls the said method. So if you introduce another inheritance level TClass2b between TClass2 and TClass3 it will skip that aswell.

Check class name

I don't know OWNER object class name. So I must check everywhere in my codes like that :
if TObject(OWNER) is TFirstClass then begin
TFirstClass(OWNER).FirstFunction;
TFirstClass(OWNER).SecondFunction;
...
end else
if TObject(OWNER) is TSecondClass then begin
TSecondClass(OWNER).FirstFunction;
TSecondClass(OWNER).SecondFunction;
...
end;
Is there a better way? Because I must do this if condition in many place of the codes.
All functions of TFirstClass and TSecondClass (which I have to run) are the same.
Note : I use Delphi 5.
If you have no access to TFirstClass and TSecondClass, but still want to simplify your code, here's a way:
Create an adapter base class:
type
TMyAdapter = class(TObject)
public
procedure FirstMethod; virtual; abstract;
procedure SecondMethod; virtual; abstract;
end;
Then create descendant classes TFirstClassAdapter and TSecondClassAdapter and give them each a private reference to the instance of TFirstClass or TSecondClass respectively. Add a constructor which sets this reference. Override the methods of the adapter classes, so that they call through to the adapted classes.
type
TFirstClassAdapter = class(TMyAdapter)
private
fObject: TFirstClass;
public
constructor Create(AAdaptedObject: TFirstClass);
procedure FirstMethod; override;
procedure SecondMethod; override;
end;
constructor TFirstClassAdapter.Create(AAdaptedObject: TFirstClass);
begin
inherited Create;
fObject := AAdaptedObject;
end;
procedure TFirstClassAdapter.FirstMethod;
begin
fObject.FirstMethod;
end;
procedure TFirstClassAdapter.SecondMethod;
begin
fObject.SecondMethod;
end;
Same for the other class. Now you only need to decide whether you create the adapter once and pass it around, or whether you make a function that you call everywhere you need it, and which will give you an adapter for your concrete class.
If you implement the adapter using interfaces, then you will not even need to manage the lifetime of the adapter yourself.
This way you can have the polymorphic behaviour that Ulrich gave in his answer, but without the need to change TFirstClass and TSecondClass.
Derive TFirstClass and TSecondClass from a common base class that declares virtual methods FirstFunction and SecondFunction.
Uli.
At first excuse-me for my bad english.
If you can't do the 2 before responses (Adapters and derive from a base class), you can use RTTI to access a procedure by it's name.
The procedure must be declared in the published section.
If you've a declaration like this:
TFirstClass = class(TObject)
published
procedure FirstFunction;
procedure SecondFunction;
end;
TSecondClass = class(TObject)
published
procedure FirstFunction;
procedure SecondFunction;
end
You can do something like this to execute a method if you have the name:
// Acceso a la rutina; TObject is a Base class for
// TFirstClass and TSecondClass
Routine.Data := Pointer(obj as TObject);
// Devuelve la dirección de un método published; Method for it's name
Routine.Code := (obj as TObject).MethodAddress('SecondFunction');
// Not find
if (Routine.Code = nil) then Exit;
// execute
TExecuteMethod(Routine);
You can see similar codes here:
* Tip4
* Tip7
Regards.

Resources