[gelöst]Object? Class? Class(TObject)? Was jetzt?

Für Fragen von Einsteigern und Programmieranfängern...
Nimral
Beiträge: 390
Registriert: Mi 10. Jun 2015, 11:33

[gelöst]Object? Class? Class(TObject)? Was jetzt?

Beitrag von Nimral »

Ich habe heute eine Weile lang gegen TFPGList und TFPGObjectList gekämpft, bis ich irgendwann bemerkt habe, dass ich bei der Definition des Generic einen kleinen Gewohnheitsfehler begangen hatte:

Code: Alles auswählen

Type
  TPatient = object
    private
    public
  end;
  
  .... vs ...
  
  Type
  TPatient = class
    private
    public
  end;
Erstere Definition ist dann "kompatibel" mit TFPGList, zweitere mit TFPGObjectList. Irgendwann habe ich das geschnallt und meinen Code ans Laufen bekommen. Gut. Halber Tag versenkt, es ist nicht der Erste und wird nicht der Letzte sein im Dickicht der OOP.

Dann habe ich versucht, den genauen Unterschied zwischen den Beiden zu ergründen. Dass das Eine auf dem Heap wohnt und das Andere auch, aber zusätzlich (?) einen Pointer auf dem Stack verwendet, bringt mich leider nicht weiter (--> https://www.tutorialspoint.com/pascal/p ... iented.htm)

Mit dem Wiki (bzw, in diesem Fall, DEN Wikis, die sich teilweise widersprechen) und der Doku (die sich mit der Unterscheidung der Beiden überhaupt nicht aufhält) komme ich nicht weiter.

Wer kann helfen und dabei eventuell erläutern, warum sich der Autor der fgl dazu entschlossen hat, beide Sprachelemente einzusetzen und irgendwie dubios zu vermischen. Siehe z.B. TFPGList: da wird class(TObject) verwendet???

Armin.
Zuletzt geändert von Nimral am Mi 19. Jan 2022, 09:37, insgesamt 1-mal geändert.

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

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von theo »

Verstehe nicht genau.

Nur soviel: TObject ist die Basisklasse aller Klassen und hat mit "type TTest = object" nichts zu tun.
Type Object ist etwas anderes (ein besserer Record, gilt seit Delphi als veraltet) und das brauchst du wahrscheinlich gar nicht.
https://wiki.freepascal.org/Object

TFPGObjectList hat mMn mit dem Type "Object" nichts zu tun sondern meint die Basisklasse TObject, also auch irgend eine abegeleitete Klasse.
https://www.freepascal.org/docs-html/rt ... bject.html

Nimral
Beiträge: 390
Registriert: Mi 10. Jun 2015, 11:33

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von Nimral »

Hm.

https://www.freepascal.org/docs-html/cu ... fse28.html

liest sich anders. Das was da steht entspricht ziemlich genau dem was man auch unter "Class" (Kap. 6) lesen kann. Alles da was OOP Herzen höher schlagen lässt - genau so wie bei "Class".

https://www.freepascal.org/docs-html/cu ... 0-940006.1

hat auch noch einen Beitrag zur großen Verwirrung zu leisten:
In MacPas mode, the Object keyword is replaced by the class keyword for compatibility with other pascal compilers available on the Mac. That means that in MacPas mode, the reserved word “class” in the above diagram may be replaced by the reserved word “object”.
Object und Class scheinen sowohl beide in Verwendung zu sein, als auch weithend die gleichen Funktionalitäten zu bieten. Irgendwie sind das zwei Parallelwelten?

Dass das Root-Objekt aller Objekte, die per "class" definiert wurden, TObject heißt, hilft auch nicht gerade ...

Armin.

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

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von fliegermichl »

Ich denke, der Hauptunterschied ist der, daß Objekte statisch genutzt werden können.

Code: Alles auswählen

type
 TTest = object
  procedure hallo;
 end;
 
 TTestClass = class
  procedure Hallo;
 end;
 
var test : TTest;
 testClass : TTestClass;
begin
 test.Hallo; // Das geht
 testClass.Hallo; // Das geht nicht, da die Klasse erst erzeugt werden muss
 testClass := TTestClass.Create;
 testClass.Hallo; // Jetzt geht es
 testClass.Free;
end;
Edit: Ich hab das grad mal probiert und der erste Aufruf von der nicht initialisierten Klasse testClass.Hallo funktioniet doch :shock:

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

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von theo »

Nimral hat geschrieben: Di 18. Jan 2022, 16:05 Irgendwie sind das zwei Parallelwelten?
Ja!
Embarcadero (Delphi) meint zu Objects:
Object types are supported for backward compatibility only. Their use is not recommended.

Benutzeravatar
kupferstecher
Beiträge: 431
Registriert: Do 17. Nov 2016, 11:52

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von kupferstecher »

Der (Haupt-) Unterschied ist, dass beim Typ object der Speicher für die Instanz statisch reserviert wird (also wie beim Record), und beim Typ class wird der Speicher für die Instanz beim Aufruf des Konstruktors auf dem Heap alloziert. Die Variable eines Typs class ist (im Gegensatz zu object) nur eine Referenz auf den Speicherbereich.

@fliegermichel, der Compiler fügt wohl keinen Code ein, um zu überprüfen, ob es die Instanz überhaupt gibt. Es wird in dem Fall halt ein falscher Self-Parameter übergeben. Also wehe du benutzt in der Hallo-Methode eine Variable aus der Klasse, dann kommt die Zugriffsverletzung.

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

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von theo »

Naja, es kracht schon mit "Einstellungen -> Debuggen -> Überprüfen -> Methodenaufrufe prüfen (-CR)"

Aber es gibt natürlich noch Klassenmethoden, die laufen auch ohne Instanz.

Code: Alles auswählen

...
 TTestClass = class
   class procedure cHallo;
   procedure Hallo;
 end;   
....
procedure TForm1.Button1Click(Sender: TObject);
var TestClass:TTestClass;
begin
  TTestClass.cHallo; //Funzt
  TestClass.Hallo; //Kracht mit -CR
end;
...
{ TTestClass }

class procedure TTestClass.cHallo;
begin
  ShowMessage(Self.ClassName);
end;

procedure TTestClass.Hallo;
begin
  ShowMessage(Self.ClassName);
end;    

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

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von fliegermichl »

Stimmt, hab es grad ausprobiert. Im Fall der nicht erzeugten Klasse ist self = nil.

Nimral
Beiträge: 390
Registriert: Mi 10. Jun 2015, 11:33

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von Nimral »

Ok dank euch mal wieder herzlichst für die Klarstellunugen :-)

Eins noch: was mich aus der Schiene geworfen hat war letzendlich auch noch

Code: Alles auswählen

Type
  TTest = class(TObject)
    private
    public
  end;

... vs ...
  
  TTest = class
    private
    public
  end;
  
  ... vs ...
  
  TTest = object(TObject)  // geht nicht: Error: The mix of different kind of objects (class, object, interface, etc) isn't allowed
    private
    public
  end;  
  
Wenn ich das richtig geschnallt habe, sind die beiden Oberen absolut identisch, bzw. class wird, wenn keine Vererbung angegeben wird, automatisch zu class(TObject). Und object steht neidisch daneben und darf nicht mitspielen mit den Anderen.

--> Ich versuche, object zu vergessen. Alter Zopf, kann ab.

Armin.

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

Re: Object? Class? Class(TObject)? Was jetzt?

Beitrag von theo »

Nimral hat geschrieben: Mi 19. Jan 2022, 09:37 Wenn ich das richtig geschnallt habe, sind die beiden Oberen absolut identisch, bzw. class wird, wenn keine Vererbung angegeben wird, automatisch zu class(TObject).
Genau.
If no parent class name is specified, the class inherits directly from TObject.

PascalDragon
Beiträge: 954
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: [gelöst]Object? Class? Class(TObject)? Was jetzt?

Beitrag von PascalDragon »

Das Objektmodell, dass auf object aufbaut stammt aus der Zeit von Turbo Pascal 6. Diese verhalten sich prinzipiell ähnlich zu Records, als dass sie sowohl auf dem Stack liegen können (oder eine statische Variable sein können), als auch, dass man einen Zeiger darauf haben kann und sie dynamisch anlegt. Im dynamischen Fall wird dann auch Polymorphie unterstützt (v.a. Verwendung von virtuellen Methoden).

Das Objektmodell, dass auf class aufbaut wurde mit Delphi 1 eingeführt. Hier sind die Objektinstanzen immer auf dem Heap und werden mit Hilfe eines Konstruktors angelegt. Dieses Objektmodell unterstützt viel mehr Funktionalitäten als es object kann (Möglichkeit den Typ abzufragen, Implementierung von Interfaces, published Properties mit Informationen, die zur Laufzeit abgefragt werden können (worauf die LCL aufbaut), um mal drei zu nennen). Auch haben Klassen immer den Typ System.TObject als Basis, während object keinen Basistyp hat (die haben dann einfach keinen Parent).

Es ist nicht möglich diese beiden zu mischen (also im Sinne, dass ein object von einer class ableitet und umgekehrt).

Dieses Objektmodell hat das auf object aufbauende im Großen und Ganzen verdrängt. Das heißt, wenn von einem object die Rede ist, dann ist die Wahrscheinlichkeit groß, dass es sich auf eine Objektinstanz einer Klasse bezieht. Dies ist zum Beispiel bei den verschiedenen Container-Klassen der Fall: TFPGList<> bezieht sich auf primitive Typen und Records (aber auch object Typen), während TFPGObjectList<> sich auf Objektinstanzen vom Typ class bezieht. Ähnliches trifft auf TList und TObjectList aus der Unit Contnrs zu.

Dein Beispiel mit dem TTest = object(TObject) würde übrigens funktionieren, wenn du die Unit Objects verwendest, da dort ein TObject = object deklariert ist, welche als Basis für TP basierten Code dient (zum Beispiel Free Vision, welches von der Textmode IDE genutzt wird und noch auf object basiert, baut darauf auf). In der gleichen Unit würde dann allerdings ein TTest = class(TObject) zu einem Fehler führen. :P
Nimral hat geschrieben: Di 18. Jan 2022, 16:05 https://www.freepascal.org/docs-html/cu ... 0-940006.1

hat auch noch einen Beitrag zur großen Verwirrung zu leisten:
In MacPas mode, the Object keyword is replaced by the class keyword for compatibility with other pascal compilers available on the Mac. That means that in MacPas mode, the reserved word “class” in the above diagram may be replaced by the reserved word “object”.
Object und Class scheinen sowohl beide in Verwendung zu sein, als auch weithend die gleichen Funktionalitäten zu bieten. Irgendwie sind das zwei Parallelwelten?
Der Teil ist nur relevant, wenn du tatsächlich den Mac Pascal Dialekt einsetzt. Der Hintergrund ist, dass Mac Pascal sich parallel zu Turbo Pascal entwickelt hat und die ihren eigenen object Typ eingeführt haben. Allerdings verhielt sich dieser von vornherein mehr wie Delphi's class denn als TP's object, weswegen in FPC der Compiler im Mac Pascal Modus zwar object liest, aber letztlich intern ein class draus macht. ;)
FPC Compiler Entwickler

Nimral
Beiträge: 390
Registriert: Mi 10. Jun 2015, 11:33

Re: [gelöst]Object? Class? Class(TObject)? Was jetzt?

Beitrag von Nimral »

*Das* gehört irgendwo ins Wiki, ganz, ganz weit vorne ...

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

Re: [gelöst]Object? Class? Class(TObject)? Was jetzt?

Beitrag von theo »

Nimral hat geschrieben: Mi 19. Jan 2022, 10:31 *Das* gehört irgendwo ins Wiki, ganz, ganz weit vorne ...
Naja, normalerweise kommt man nur auf den Objekt Typ, wenn man wie du darüber stolpert.
Eigentlich wird der totgeschwiegen. :wink:

Warf
Beiträge: 2118
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: [gelöst]Object? Class? Class(TObject)? Was jetzt?

Beitrag von Warf »

Ein Grund für diese Verwirrung ist das im OOP Sprech die Instanzen von Klassen auch Objects genannt werden. Also der Grund warum TObjectList so heist ist weil es eine Liste von Objekten, im sinne von instanzen von Klassen ist.

Objects (als Datenstruktur) hingegen waren der erste OOP Ansatz für Pasca. Später hat Borland sich dann gedacht das objects zu kompliziert für den Durchschnittsprogrammierer sind und haben dann Classes eingebaut die im Grunde das gleiche sind nur mit Compiler magic um die pointer zu verstecken.
Im grunde ist dieser code:

Code: Alles auswählen

type
  TTestClass = class
  private
    FData: Integer;
  public
    constructor Create(AData: Integer);
    destructor Destroy; override;
    procedure Foo;
  end;

...
var test: TTestClass;
begin
  test := TTestClass.Create(42);
  test.Foo;
  test.Free;
end;
Semantisch nicht groß anders als:

Code: Alles auswählen

type
  PTestObject = ^TTestObject;
  TTestObject = object
  private
    FData: Integer;
  public
    constructor Init(AData: Integer);
    destructor Final;
    procedure Foo;
  end;

...
var test: PTestObject ;
begin
  new(test);
  test^.Create(42);
  test^.Foo;
  test^.Final;
  dispose(test);
end;
Man kann Klassensyntax mit Objekten sogar sehr gut approximieren:

Code: Alles auswählen

{$ModeSwitch autoderef} // Damit man ^  beim zugriff weglassen kann

type
  PTestObject = ^TTestObject;
  TTestObject = object
  private
    FData: Integer;
  public
    class function Create(AData: Integer): PTestObject; static;
    constructor Init(AData: Integer);
    destructor Final;
    procedure Free;
    procedure Foo;
  end;

...
class function TTestObject.Create(AData: Integer): PTestObject;
begin
  New(Result);
  Result.Init(AData);
end;

function TTestObject.Free;
begin
  if Assigned(@Self) then
  begin
    Final;
    FreeMem(@self);
  end;
end;
...
var test: PTestObject ;
begin
  test := TTestObject.Create(42);
  test.Foo;
  test.Free;
end;
Und schon sieht es genauso aus wie eine Class.

Zwar hat Embarcadero objects deprecated, die FPC entwicklung allerdings nicht, der Grund dafür ist das Objects zum einen noch sehr Nützlich sein können, zum anderen weil sie auch neue Features bekommen. Z.B. Da Objects speicherunabhängig sind, d.h. während classes immer auf dem heap liegen müssen können Objects auf den Heap gelegt werden (siehe Beispiel oben), aber auch überall sonst hin, wie den stack, shared memory, dateien, etc. Das erlaubt sachen die mit Classes nicht möglich sind, z.B. die verwendung von Operatoren:

Code: Alles auswählen

type
  TTestObject = object
    ...
  end;
  
operator +(const A, B: TTestObject): TTestObject;

...
var
  A, B, C: TTestObject;
begin
  A := ...
  B := ...
  C := A + B;
end.
Das gesagt, Classes haben einige Features bekommen die Objects nicht haben, vor allem die verwendung von Interfaces und RTTI.

Beide haben also ihren Nutzen, und daher würde ich nicht empfehlen Objects einfach zu vergessen.

Persönlich finde ich sogar das die Entscheidung von Objects auf Classes zu wechseln die schlechteste Entscheidung für Delphi war, denn theoretisch könnte man alles was Classes können auch für objects implementieren (also Interfaces, RTTI, etc.), aber durch die limitierung von Classes auf den Heap sind viele Sachen die Objects können einfach konzeptionell nicht möglich. Classes sind strikt schlechter als Objects. Z.B. die Operatoren von oben gehen einfach nicht da Classes auf dem Heap sind und Pascal manuelle Speicherverwaltung benötigt.

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

Re: [gelöst]Object? Class? Class(TObject)? Was jetzt?

Beitrag von theo »

Und damit es nicht zu einfach wird, sollte man noch "advanced records" ins Spiel bringen, welche sich dann auch noch mit der Funktionalität des Objekttyps überlappen. :mrgreen:
https://forum.lazarus.freepascal.org/in ... ic=30686.0

viewtopic.php?f=29&t=5894

Antworten