[gelöst] Verwendung von class of mit und ohne explizitem Typecast

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1805
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

[gelöst] Verwendung von class of mit und ohne explizitem Typecast

Beitrag von fliegermichl »

Das folgende Programm erstellt eine Kopie einer Klasse unter Verwendung von class of.
Dabei einmal mit explizitem Typecast und einmal ohne.

Bei der Verwendung ohne den Typecast wird zwar die korrekte Klasse erzeugt, aber deren Constructor nicht aufgerufen.
Soll das so sein und wenn ja - wieso?

Das Problem ist, daß imn zweiten Fall nur TObject.Create aufgerufen wird und damit fList = nil ist.

Code: Alles auswählen

program Project1;
uses classes;

type

  { TBase }

  TBase = class ( TList )
  private
    fName : string;
  public
    constructor Create; virtual;
    function GetCopy1 : TBase;
    function GetCopy2 : TBase;
    property Name : string read fName;
  end;

  TBaseClass = class of TBase;

{ TBase }

constructor TBase.Create;
begin
  inherited Create;
  fName := 'TBase';
end;

function TBase.GetCopy1: TBase;
begin
  Result := TBase(TBaseClass(classType).Create);
end;

function TBase.GetCopy2: TBase;
begin
  Result := TBase(classType.Create);
end;

var a, b, c : TBase;

begin
  a := TBase.Create;
  b := a.GetCopy1;
  c := a.GetCopy2;
  writeln('a: ', a.Name + ' (' + a.Classname + ')');
  writeln('b: ', b.Name + ' (' + b.Classname + ')');
  writeln('c: ', c.Name + ' (' + c.Classname + ')');
  ReadLn;
end.
Das Programm erzeugt folgende Ausgabe:

Code: Alles auswählen

a: TBase (TBase)
b: TBase (TBase)
c:  (TBase)
Zuletzt geändert von fliegermichl am Mi 29. Jul 2026, 10:07, insgesamt 1-mal geändert.

Rheinländer
Beiträge: 9
Registriert: Di 1. Okt 2024, 13:05

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von Rheinländer »

Hallo, könnte die Deklaration Create der Grund sein!

Falsch
constructor Create; virtuale;

Richtig
constructor Create; override;

Benutzeravatar
theo
Beiträge: 11379
Registriert: Mo 11. Sep 2006, 19:01

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von theo »

Rheinländer hat geschrieben: Mo 27. Jul 2026, 11:04 Hallo, könnte die Deklaration Create der Grund sein!

Falsch
constructor Create; virtuale;

Richtig
constructor Create; override;
Das geht ja nicht mit TList als Vorfahr:
Error: There is no method in an ancestor class to be overridden: "constructor Create;"

@fliegermichl: Ich finde es immer hilfreich, wenn man das Ziel der ganzen Übung versteht.
Oder ist das "Grundlagenforschung"? :wink:

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1805
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von fliegermichl »

Rheinländer hat geschrieben: Mo 27. Jul 2026, 11:04 Hallo, könnte die Deklaration Create der Grund sein!

Falsch
constructor Create; virtuale;

Richtig
constructor Create; override;
Ich denke nicht, da der Constructor von TList statisch deklariert ist. override würde dadurch einen Compilerfehler verursachen.

Code: Alles auswählen

project1.lpr(9,17) Error: There is no method in an ancestor class to be overridden: "constructor create;"

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1805
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von fliegermichl »

theo hat geschrieben: Mo 27. Jul 2026, 11:59
Rheinländer hat geschrieben: Mo 27. Jul 2026, 11:04 Hallo, könnte die Deklaration Create der Grund sein!

Falsch
constructor Create; virtuale;

Richtig
constructor Create; override;
Das geht ja nicht mit TList als Vorfahr:
Error: There is no method in an ancestor class to be overridden: "constructor Create;"
Sorry, ich hatte deinen Post übersehen.
@fliegermichl: Ich finde es immer hilfreich, wenn man das Ziel der ganzen Übung versteht.
Oder ist das "Grundlagenforschung"? :wink:
Ich hatte das in meinem Programm und habe mich gewundert, wieso ich Zugriffsverletzungen erhalte, wenn ich auf die Items der Kopie zugreifen wollte.
TList hat ein Feld fList vom Typ TFPList, welches in TList.Create erzeugt und im Destroy wieder freigegeben wird.

Da durch die Erzeugung der Klasse ohne den expliziten Typecast ja die richtige Klasse erzeugt wurde, nur eben ohne deren Constructor aufzurufen, wurde eben auch fList nicht zugewiesen. Der Fehler war schwer zu finden und ich fragte mich, wozu das unterschieden wird.

die Eigenschaft classtype liefert ja auch ohne Typecast richtig TBase und erzeugt ja auch eine Instanz dieser Klasse.

Ich halte das für einen Compilerfehler.
Mindestens aber mal eine Warnung wert.

Was mich wundert ist, daß es der Delphicompiler genauso macht.
Meine Frage daher: Warum?

Benutzeravatar
theo
Beiträge: 11379
Registriert: Mo 11. Sep 2006, 19:01

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von theo »

Die Frage ist schon berechtigt und ich kenne die Antwort auch nicht, oder habe sie vergessen... :wink:

Trotzdem verstehe ich nicht, was der ganze Zirkus soll.
Warum nicht einfach:

Code: Alles auswählen

function TBase.GetCopy2: TBase;
begin
   Result := TBase.Create;   
end;   

Benutzeravatar
Zvoni
Beiträge: 717
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz/FPC fixes)
CPU-Target: 64Bit
Wohnort: BW

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von Zvoni »

Ich glaube hier liegt ein grundlegendes Missverständnis vor was Referenztypen für Klassen betrifft.

Siehe hier: https://www.freepascal.org/docs-html/re ... 0-940006.1

Um im Beispiel zu bleiben, hier abgeändert.

Code: Alles auswählen


  TBase = Class;
  TBaseClass = class of TBase;
  
 TBase = class ( TList )
  private
    fName : string;
  public
    constructor Create; virtual;
    function GetCopy1(AClass:TBaseClass) : TBase;   
    property Name : string read fName;
  end;
  
  TKind = Class(TBase)
  //Irgendwas
  End;
  
  
constructor TBase.Create;
begin
  inherited Create;
  fName := 'TBase';
end;

function TBase.GetCopy1(AClass:TBaseClass): TBase;
begin
  Result := AClass.Create);
end;  
  
  
 Var
   B:TBase;
   K:TBase;
 Begin
   B:=TBase.Create;
   K:=B.GetCopy1(TKind);
   Writeln(K.ClassName);
End;   
Heisst: Auch wenn K als TBase in Var steht, bekommt es Klassentyp TKind zur LAUFZEIT

Alles ungetestet.
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

shokwave
Beiträge: 490
Registriert: Do 15. Nov 2007, 16:58
OS, Lazarus, FPC: Win11/Ubuntu Budgie/macOS (Laz 4.x FPC 3.2.x)
CPU-Target: i386, x64, arm64
Wohnort: Gera

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von shokwave »

Also so wie ich es verstehe liefert ClassType ja nur den Teil der Klasse der in den Felder von TClass gespeichert ist. D.h. deinen Code kann man auch so schreiben:

Code: Alles auswählen

  function TBase.GetCopy1: TBase;
  begin
    // Der TypeCast zu TBase ist überflüssig und der TypeCast von 
    // ClassType auch, da du erst auf TBaseClass das Create aufrufst
    Result := TBaseClass.Create;
  end;

  function TBase.GetCopy2: TBase;
  var
    PureClass: TClass;
    PureObject: TObject;
  begin
    // Der ReturnType von ClassType ist TClass
    PureClass := ClassType;  
    // Du rufst Create auf TClass auf und erhälst damit ein TObject
    PureObject := PureClass.Create;  
    // Der TypeCast ändert zwar den Typ, aber ruft Create nicht erneut auf dem neuen Typ auf
    Result := TBase(PureObject); 
  end;
mfg Ingo

Benutzeravatar
Zvoni
Beiträge: 717
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz/FPC fixes)
CPU-Target: 64Bit
Wohnort: BW

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von Zvoni »

Lest doch einfach mal die Dokumentation.....
Class reference types are used to create instances of a certain class, which is not yet known at compile time, but which is specified at run time. Essentially, a variable of a class reference type contains a pointer to the definition of the specified class.
Zu Deutsch:
Klassen-Referenz-TYPEN werden benutzt, um Instanzen einer bestimmten Klasse zu erzeugen, welche zur Kompilierzeit noch nicht bekannt sind, sondern erst zur Laufzeit.
Im Prinzip, eine VARIABLE eines Klassen-Referenz-TYPES beinhaltet einen Zeiger auf die DEFINITION der genannten Klasse.

NICHT AUF EINE INSTANZ!

Aus meinem Beispiel oben:

Code: Alles auswählen

function TBase.GetCopy1(AClass:TBaseClass): TBase;
begin
  Result := AClass.Create;
end;  
Argument "AClass" ist kein Zeiger auf eine Instanz, sondern auf einen TYPEN
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Benutzeravatar
theo
Beiträge: 11379
Registriert: Mo 11. Sep 2006, 19:01

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von theo »

Nur kurz: Ich glaube die Konfusion liegt in der Frage, was Self.ClassType eigentlich ist.
Anscheinend nicht das, was man erwarten würde.

Benutzeravatar
Zvoni
Beiträge: 717
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz/FPC fixes)
CPU-Target: 64Bit
Wohnort: BW

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von Zvoni »

theo hat geschrieben: Di 28. Jul 2026, 09:50 Nur kurz: Ich glaube die Konfusion liegt in der Frage, was Self.ClassType eigentlich ist.
Anscheinend nicht das, was man erwarten würde.
Kann natürlich auch sein.
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1805
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von fliegermichl »

Code: Alles auswählen

function TBase.GetCopy1(AClass:TBaseClass): TBase;
begin
  Result := AClass.Create;
end;  
Natürlich kann man das so machen.
Ich habe aber eine Basisklasse von der hunderte Kindklassen abgeleitet wurden. Diese sind treeförmig strukturiert.
Um jetzt eine Kopie eines Astes, Zweiges oder ganzen Baumes zu bekommen, definiere ich einmal die Function GetCopy und einmal die Procedure Assign.
GetCopy hat die Aufgabe eine neue Instanz der gleichen Klasse zu erzeugen und Assign überträgt alle aktuellen Properties auf die Kopie.
Assign ist virtual und muss natürlich in jeder Subclass überschrieben werden, sofern sie neue Properties einführt.
Für GetCopy langt eine Definition in der Basisklasse.

Benutzeravatar
Zvoni
Beiträge: 717
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz/FPC fixes)
CPU-Target: 64Bit
Wohnort: BW

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von Zvoni »

OK, habs mir nochmal angeschaut.

und glaube es gefunden zu haben

Code: Alles auswählen

function TBase.GetCopy2: TBase;
begin
  //Result := TBase(classType.Create); //--> Deine Version
  Result := TBaseClass(classType).Create;  //Meine Version
end;
Code mit welchem ich getestet habe.
Hinweis: Im Constructor weise ich ClassName an fName statt des konstanten Strings, zur besseren Unterscheidung

Code: Alles auswählen

program Project1;
{$mode objfpc}{$H+}
Uses Sysutils, Classes;
Type
  TBase = class;
  TBaseClass = class of TBase;

  TBase = class (TList)
  private
    fName : string;
  public
    constructor Create;
    function GetCopy1(AClass:TBaseClass) : TBase;
    function GetCopy2 : TBase;
    property Name : string read fName;
  end;

  TSohn=Class(TBase)

  end;
  TTochter=Class(TBase)

  end;

constructor TBase.Create;
begin
  inherited Create;
  fName := ClassName;
end;

function TBase.GetCopy1(AClass: TBaseClass): TBase;
begin
  Result := AClass.Create;
end;

function TBase.GetCopy2: TBase;
begin
  Result:=TBaseClass(ClassType).Create;
end;

Var
  a,s1,s2,s3,t1,t2,t3:TBase;
begin
  a:=TBase.Create;
  s1:=TSohn.Create;
  s2:=s1.getcopy1(TSohn);
  t1:=TTochter.Create;
  t2:=t1.Getcopy1(TTochter);
  Writeln(a.Name);
  Writeln(s2.Name);
  Writeln(t2.Name);
  Writeln('------------');
  s3:=s1.getcopy2;
  t3:=t1.Getcopy2;
  Writeln(s3.Name);
  Writeln(t3.Name);
  Readln;

end.
Ergebnis
TBase
TSohn
TTochter
------------
TSohn
TTochter
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

PascalDragon
Beiträge: 1046
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von PascalDragon »

fliegermichl hat geschrieben: Mo 27. Jul 2026, 10:23 Das folgende Programm erstellt eine Kopie einer Klasse unter Verwendung von class of.
Dabei einmal mit explizitem Typecast und einmal ohne.

Bei der Verwendung ohne den Typecast wird zwar die korrekte Klasse erzeugt, aber deren Constructor nicht aufgerufen.
Soll das so sein und wenn ja - wieso?

Das Problem ist, daß imn zweiten Fall nur TObject.Create aufgerufen wird und damit fList = nil ist.
Ja, das soll so, da der Typ von TObject.ClassType TClass ist und TClass hat keinen virtuellen Konstruktor. Wenn du ClassType zu TBaseClass castest, dann wird durch Create der virtuelle Konstruktor von TBase aufgerufen.
FPC Compiler Entwickler

Socke
Lazarusforum e. V.
Beiträge: 3193
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Verwendung von class of mit und ohne explizitem Typecast

Beitrag von Socke »

Dann liegt die ganze Verwirrung darin begründet, dass ...
  • TObject.ClassType ein TClass zurückliefert
  • TClass = class of TObject definiert ist
  • TObject.Create nicht virtuell ist
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Antworten