[fpc-pascal] class inheritance and type incompatibility

Sven Barth pascaldragon at googlemail.com
Thu Sep 26 14:02:57 CEST 2013


On 26.09.2013 08:39, Xiangrong Fang wrote:
> Hi All,
>
> I have the following program:
>
>    1 program test;
>    2 {$mode objfpc}{$H+}
>    3 uses tree;
>    4 type
>    5   TIntTree = class(specialize TTree<Integer>)
>    6   public
>    7     function Clone: TIntTree;
>    8   end;
>    9 function TIntTree.Clone: TIntTree;
>   10 begin
>   11   Result := TIntTree(inherited Clone);
>   12 end;
>   13 var
>   14   it1, it2 : TIntTree;
>   15 begin
>   16   it1 := TIntTree.Create(1, nil);
>   17   it2 := it1.Clone;
>   18   WriteLn(it1.ClassName);
>   19   WriteLn(it2.ClassName);
>   20 end.
>
> Which output:
>
> TIntTree
> TIntTree.TTree$LongInt
>
> The source code of TTree is here:
> https://github.com/xrfang/fpcollection/blob/master/src/units/tree.pas
>
> Why the typecast on line 11 does not work? How to modify the code so
> that the cloned object has same type as the original one?

Please always think through this without generics.
Your code looks roughly like this:

=== code begin ===

type
   TTreeInteger = class
     function Clone: TTreeInteger;
   end;

   TTreeInt = class(TTreeInteger)
     function Clone: TTreeInt;
   end;

function TTreeInteger.Clone: TTreeInteger;
begin
   Result := TTreeInteger.Create;
end;

function TTreeInt.Clone: TTreeInt;
begin
   Result := TTreeInt(inherited Clone);
end;

=== code end ===

As you can see you are constructing a TTreeInteger class inside 
TTreeInteger.Clone and nothing in the world afterwards can turn it into 
a TTreeInt. If you'd use the "as" operator in TTreeInt.Clone then you'd 
even get an exception, because you can not assign a TTreeInteger 
instance(!) to a TTreeInt.

What should work is the following (now with generics again):

=== code begin ===

type
   TTree<T> = class
   private type
     TSelfClass = class of TTree;
   public
     function Clone: TTree;
   end;

function TTree.Clone: TTree;
begin
   Result := TSelfClass(Self.ClassType).Create(Data, FParent);
   (* ... *)
end;

=== code end ===

Note: The cast to TSelfClass is necessary to have access to the 
overloaded constructor you added for TTree<>.

Regards,
Sven



More information about the fpc-pascal mailing list