Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Klassen-Funktion benutzen, ohne die Klasse zu erstellen (https://www.delphipraxis.net/156049-klassen-funktion-benutzen-ohne-die-klasse-zu-erstellen.html)

x000x 18. Nov 2010 08:41

Delphi-Version: 5

Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Moin moin,

ich habe folgende Dummy Klasse und wundere mich gerade, warum das funktioniert. Ich hätte gerne gewusst warum so etwas funktioniert.
Beim debuggen ist FTest = nil und trotzdem gibt es keine Zugriffsverletzung und die Klassenfunktion wird ausgeführt.

Das dieses wohl nicht wirklich Sinn macht ist mir schon klar, mir geht es einzig nur darum, warum das funktioniert?!
Könnte mir das bitte jemand erklären?

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
   TDummyClass = class
   public
      procedure Test(var paStrString : String);
   end;

type
  TForm1 = class(TForm)
    btn: TButton;
    procedure btnClick(Sender: TObject);
  private
    { Private-Deklarationen }
    FTest: TDummyClass;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;


implementation

{$R *.DFM}

procedure TForm1.btnClick(Sender: TObject);
var
   str: String;
begin
   str := 'Test';
   FTest.Test(str);
   ShowMessage(str);
end;

{ TDummyClass }

procedure TDummyClass.Test(var paStrString: String);
begin
   paStrString := paStrString + 'JA';
end;

end.

mkinzler 18. Nov 2010 08:44

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Klasenmethoden funktionieren unabhängig von Instanzen. allerdings handelt es sich in deinem Fall um eine normale Methode

guinnes 18. Nov 2010 08:45

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Du greifst auf keine Felder der Klasse zu, darum gehts. Dazu ist die Methode nicht virtuel.

x000x 18. Nov 2010 09:03

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von mkinzler (Beitrag 1062270)
Klasenmethoden funktionieren unabhängig von Instanzen. allerdings handelt es sich in deinem Fall um eine normale Methode

Ja, das ist mir bekannt - dann hätte ich class davor schreiben müssen und der Aufruf wäre dann TDummyClass.Test. Das steht dort aber nicht und trotzdem funktionierts.

Werden denn beim Erstellen einer Instanz mit
Delphi-Quellcode:
FTest:= TDummyClass.Create;
nur die Felder initialisiert? Vermutlich ja, dann macht das auch Sinn das es funktioniert.
Sobald ich dann aber auf Felder der Klasse zugreife, dann knallts.

Ok, ich denke ich verstehe... Vielen Dank

guinnes 18. Nov 2010 09:05

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von x000x (Beitrag 1062275)
Werden denn beim Erstellen einer Instanz mit
Delphi-Quellcode:
FTest:= TDummyClass.Create;
nur die Felder initialisiert?

Es wird auch die Adresse der VMT in die Instanz kopiert. Darum geht ein Zugriff auf eine Virtuelle Methode ohne Create auch schief

x000x 18. Nov 2010 09:15

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von guinnes (Beitrag 1062276)
Es wird auch die Adresse der VMT in die Instanz kopiert. Darum geht ein Zugriff auf eine Virtuelle Methode ohne Create auch schief

Ok, danke

himitsu 18. Nov 2010 10:31

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von guinnes (Beitrag 1062276)
Es wird auch die Adresse der VMT in die Instanz kopiert. Darum geht ein Zugriff auf eine Virtuelle Methode ohne Create auch schief

Es wird auch der Speicher für die Klasse erstellt, worin die Felder liegen.

PS: Das Ganze ist ein klassisches Beispiel, für eine Klassenmethode, welche man ohne Instanz nutzen kann.
Delphi-Quellcode:
TDummyClass = class
public
  class procedure Test(var paStrString: String);
end;


TDummyClass.Test(xxx);
Also, wie vielleicht aus den Beiträgen ersichtlich war, ist dein Code rein zufällig gelaufen ... besser wäre eine Klassenmethode gewesen.

x000x 18. Nov 2010 10:52

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von himitsu (Beitrag 1062288)
Also, wie vielleicht aus den Beiträgen ersichtlich war, ist dein Code rein zufällig gelaufen ... besser wäre eine Klassenmethode gewesen.

Warum zufällig? Zufällig heisst doch, dass es mal funktioniert und mal nicht?! Das Beispiel funktioniert immer (solange halt in der klassenmethode keine Felder der Klasse benutzt werden)

Ein Kollege hat mich darauf angesprochen und ich war der Meinung, dass es nicht funktionieren kann - da ja keine Instanz erzeugt wurde. Und ich wurde eines besseren belehrt.
Das Code-Beispiel aus dem ersten Post ist meiner Meinung nach nicht zu verwenden, da es z.b. bei Programmpflege durch mehrere Programmierer zu Fehlern kommen wird.

Eigentlich dürfte so etwas
Delphi-Quellcode:
FTest.Test(str);
ohne erzeugen einer Instanz von Haus aus gar nicht funktionieren - denn wie bereits geschrieben bekommt man das gleiche Verhalten
durch Klassenmethoden erreicht und ist damit auf der sicheren Seite was die Pflege von Programmen angeht.

Medium 18. Nov 2010 11:12

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Es dürfte laut Paradigma nicht gehen. Es geht dennoch, weil nichtvirtuelle Methoden stets nur in der Klassendeklaration stehen, und auch Instanzen immer nur auf diese verweisen - sie werden nicht pro Instanz immer wieder kopiert. Die Theorie verbietet hier etwas, was technisch kein Problem wäre, jedoch zu welchen führen kann. Dass dies vom Compiler nicht bemeckert wird, sollte daher als Bug angesehen werden.

mleyen 18. Nov 2010 11:17

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von Medium (Beitrag 1062301)
Dass dies vom Compiler nicht bemeckert wird, sollte daher als Bug angesehen werden.

Du meinst vom Debugger, nicht? Der Compiler weiß ja nicht obs da instanziert ist.

guinnes 18. Nov 2010 11:21

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von Medium (Beitrag 1062301)
Dass dies vom Compiler nicht bemeckert wird, sollte daher als Bug angesehen werden.

Das kann der Compiler schlicht und ergreifend nicht prüfen. Du hast einen Instanzzeiger ( wenn der auch auf NIL weist ) und über diesen Zeiger rufst du eine Methode auf.
Anders wäre es, wenn du TIrgentwas.Methode aufrufst. Da kann der Compiler prüfen, on es sich bei der Methode um eine Klassenmethode handelt und tut das auch.
Wenn die Gültigkeit der Instanz zur Laufzeit jedes mal geprüft werden würd, würde das auch nichts bringen, weil
1. Jede Menge Zeit verplempert würde und
2. Nichts anderes ans eine Fehlermeldung geworfen werden könnte

Medium 18. Nov 2010 11:38

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Eine Fehlermeldung wäre ja schon mal was ;). Und eine Prüfung auf nil ist soooo teuer nun auch nicht, da laufen z.B. in managed Umgebungen ganz andere Dinge unsichtbar ab. Wenn die Instanz nicht nil, und dennoch ungültig ist - das ist was anderes. Dann kann ich ja auch auf die Felder zugrifen und erhalte lustige Dinge.
Und ja, der Debugger wäre wohl eher geeignet an dieser Stelle. Es müsste schlicht eine Exception her.

himitsu 18. Nov 2010 11:48

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von x000x (Beitrag 1062294)
Warum zufällig?

Ja OK, von seiten Delphis ist es "absicht", aber von seinen des Programmierers und in Anbetracht, daß er keine Ahnung hatte, was er macht, war es eher Zufall, daß er z.B. kein Feld verwendet hatte. :angle:

guinnes 18. Nov 2010 11:51

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Natürlich ist eine Prüfung auf NIL nicht so aufwändig ( mindestens 4 Zusätzliche Assembler-Anweisungen ), aber sie müßte vor jeder Benutzung der Klasse erfolgen, das macht sie so aufwändig.
Und eine Fehlermeldung zu werfen, wenn die korrekte Arbeitsweise der Klasse sichergestellt ist, finde ich recht unsinnig

Uwe Raabe 18. Nov 2010 11:58

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von Medium (Beitrag 1062315)
Eine Fehlermeldung wäre ja schon mal was ;). Und eine Prüfung auf nil ist soooo teuer nun auch nicht, da laufen z.B. in managed Umgebungen ganz andere Dinge unsichtbar ab. Wenn die Instanz nicht nil, und dennoch ungültig ist - das ist was anderes. Dann kann ich ja auch auf die Felder zugrifen und erhalte lustige Dinge.
Und ja, der Debugger wäre wohl eher geeignet an dieser Stelle. Es müsste schlicht eine Exception her.

Ich sehe hier weder einen Grund für eine Fehlermeldung, noch für eine Exception. Der Aufruf
Delphi-Quellcode:
FTest.Test(str);
wird vom Compiler implizit umgewandelt in so etwas wie
Delphi-Quellcode:
TDummyClass.Test(FTest, str);
. Solange innerhalb der Methode nicht auf Self (= FTest) zugegriffen wird, ist doch egal, ob das nil ist. Wenn ich aber darauf zugreife, wird eine Nil-Referenz ausgelöst und da wäre dann ja die Exception.

Der Compiler könnte allenfalls einen Hinweis auswerfen, daß die Methode vielleicht besser als Class-Method deklariert werden sollte, aber mehr braucht's IMHO nicht. Eine generelle Überprüfung von Self auf nil halte ich für Verschwendung.

Übrigens funktioniert auch folgendes:

Delphi-Quellcode:
FTest := nil;
FTest.Free;
Die Implementierung von Free prüft dabei explizit auf
Delphi-Quellcode:
Self <> nil
. Eine solche Überprüfung kann selbstverständlich auch in jeder anderen Methode einer Klasse erfolgen und nach Gusto auf das Ergebnis reagiert werden. Muss ja nicht immer 'ne Exception sein.

himitsu 18. Nov 2010 12:08

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
@Uwe: Es wird da nichts implizit umgewandelt.
Es bleibt ein Aufruf einer stinknormalen Methode,
nur daß "Self" eben nil ist (da hier praktischer Weise dieses von der Elternklasse so initialisiert wurde) und wenn man intern nun nichts nutzt, welches das Objekt benötigen würde, dann fällt es nicht auf.

PS: Wäre das FTest eine lokale Variable gewesen, dann hätte Self auf sonstwas stehen können und eine Prüfung auf NIL hätte meistens nichts gebracht.

guinnes 18. Nov 2010 12:18

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1062321)
Der Compiler könnte allenfalls einen Hinweis auswerfen, daß die Methode vielleicht besser als Class-Method deklariert werden sollte, aber mehr braucht's IMHO nicht.

Das Problem an der Stelle ist : Woher sollte der Compiler das wissen ?

Medium 18. Nov 2010 13:48

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Eine Exception wäre imho sehr wohl angebracht! Ich weiss, dass es technisch gesehen keinen Grund dafür gibt, es ist viel mehr eine Frage davon, in wie weit man die Paradigmen der OOP erzwingen möchte. Evtl. ist das mit Arraygrenzen vergleichbar: Es gibt technisch gesehen keinen Grund, beim Griff vor oder hinter ein Array zu meckern - der Speicher ist ja da. Es wiederpricht nur der Theorie des Arrays, welches nicht vorsieht länger zu sein, als es ist. Hier hat Delphi mit der Bereichsprüfung die Wahl geschaffen, ob man diese Hilfe haben mag oder nicht, was imho auch ein gangbarer Weg wäre. Nicht alles was praktisch möglich ist, ist mit der Theorie zu vereinbaren. Man muss sich nur für eines entscheiden.

guinnes 18. Nov 2010 14:36

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von Medium (Beitrag 1062364)
Evtl. ist das mit Arraygrenzen vergleichbar: Es gibt technisch gesehen keinen Grund, beim Griff vor oder hinter ein Array zu meckern - der Speicher ist ja da.

Schlechter Vergleich. Wenn du vor oder nach einem Array schreibst, veränderst du ungewollt andere Daten ( wenn du ließt, ließt du garantiert was falsches ). Ein Vergleich zu deinem Vergleich : :wink:
Es ist wie irgendjemanden auf der Straße in die Geldbörse zu greifen. Rein Technisch ist das Geld ja da :wink:

Medium 18. Nov 2010 16:02

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Du willst mich nicht verstehen :cry:

guinnes 18. Nov 2010 16:53

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von Medium (Beitrag 1062405)
Du willst mich nicht verstehen :cry:

Das mag so sein, oder du mich nicht ?
Es ist schon ein Unterschied, ob in der Exe das steht :
Delphi-Quellcode:
DreckMain.pas.78: Caption := fClass.Foo(S);
004666AD 8D4DF8           lea ecx,[ebp-$08]
004666B0 8B55FC          mov edx,[ebp-$04]
004666B3 8B83E0020000     mov eax,[ebx+$000002e0]
004666B9 E81AFFFFFF      call TMyClass.Foo
004666BE 8B55F8           mov edx,[ebp-$08]
004666C1 8BC3             mov eax,ebx
004666C3 E8F4CCFBFF      call TControl.SetText
oder das :
Delphi-Quellcode:
DreckMain.pas.77: if fClass <> nil then
004666AE 8BB3E0020000     mov esi,[ebx+$000002e0]
004666B4 85F6             test esi,esi
004666B6 7417             jz TForm1.Button1Click + $47
DreckMain.pas.78: Caption := fClass.Foo(S);
004666B8 8D4DF8           lea ecx,[ebp-$08]
004666BB 8B55FC          mov edx,[ebp-$04]
004666BE 8BC6             mov eax,esi
004666C0 E813FFFFFF      call TMyClass.Foo
004666C5 8B55F8           mov edx,[ebp-$08]
004666C8 8BC3             mov eax,ebx
004666CA E8EDCCFBFF      call TControl.SetText
Vor allem, weil die Funktionalität gleich ist

shmia 18. Nov 2010 17:01

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
In den Compileroptionen gibt es Bereichsüberprüfung, I/O-Prüfung und Overflow checking.
Was jetzt noch fehlt wäre ein Self-Pointer-Check.
Gibt's das eigentlich schon als Feature-Request bei Embacadero?
Während der Programmentwicklung würde man die Compileroption setzen und später wenn das Programm seine Quatlität bewiesen hat die Option wieder rücksetzen. (was Speicherplatz und Zeit spart)

himitsu 18. Nov 2010 17:31

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Du könntest ja einfach in alle Methoden dieses Einfügen.
Delphi-Quellcode:
Assert(Assigned(Self) and (TObject(Self) is TKlassentyp));
.

Aber stimmt schon, sowas könnte Emba eigentlich auch intern via Compilerschalter reinbauen.

PS: Es wäre auch schonmal schön, wenn z.B. diese Überlauf- und Bereichsprüfung standardmäßig aktiviert wäre.

schöni 18. Nov 2010 21:53

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von x000x (Beitrag 1062275)
Zitat:

Zitat von mkinzler (Beitrag 1062270)
Klasenmethoden funktionieren unabhängig von Instanzen. allerdings handelt es sich in deinem Fall um eine normale Methode

Ja, das ist mir bekannt - dann hätte ich class davor schreiben müssen und der Aufruf wäre dann TDummyClass.Test. Das steht dort aber nicht und trotzdem funktionierts.

Werden denn beim Erstellen einer Instanz mit
Delphi-Quellcode:
FTest:= TDummyClass.Create;
nur die Felder initialisiert? Vermutlich ja, dann macht das auch Sinn das es funktioniert.
Sobald ich dann aber auf Felder der Klasse zugreife, dann knallts.

Ok, ich denke ich verstehe... Vielen Dank

Die aufgerufene Methode ist ausserdem statisch, die Klasse hat keinen Vorgänger mit viertuellen Methoden. Somit ist diese Klasse auch ohne Konstruktoraufruf uneingeschränkt benutzbar. Mich wundert, das das in diesem Fall bei Feldern der Klasse nicht funktionieren soll, sofern die Felder public definiert sind. Probleme könnte es dann höchstens bei Eigenschaften geben, die ja intern im Stream gespeichert werden, was der Compiler geschickt verbirgt.

himitsu 18. Nov 2010 22:01

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von schöni (Beitrag 1062455)
Mich wundert, das das in diesem Fall bei Feldern der Klasse nicht funktionieren soll, sofern die Felder public definiert sind.

Zugriffe auf Felder und virtuelle/dynamische Methoden werden über einen Offset zu Self ausgeführt.
Ist nun Self = nil oder ein ungültiger Wert, dann geht das natürlich schief.

Medium 19. Nov 2010 01:30

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von guinnes (Beitrag 1062422)
Es ist schon ein Unterschied, ob in der Exe das steht :
{...}
oder das :
{...}
Vor allem, weil die Funktionalität gleich ist

Öhm... die Bereichsprüfung ist wohl mindestens genau so "schlimm", und da man Arrays für Gewöhnlich in Schleifen durchläuft, tendenziell sogar öfter am Zuge. Die paar Püpschen machen doch wohl, als schaltbare Option, zum Debuggen wohl kaum wat schlimmes. Im Gegenteil, es hilft ggf. sogar. Und selbst wenn es dauerhaft drin wäre, wäre es im Normalfall noch immer vernachlässigbar. Lediglich in Fällen, in denen hochoptimierter Code wirklich von Nöten ist - und dort würde ich ohnehin so gut wie geht OOP-Overhead vermeiden, und maximal auf Records setzen, wenn es schon strukturiert sein muss. Allerdings: Die meisten derartig auf Speed gezüchteten Dinge setzen auf primitive Datentypen, Arrays dieser, und inline-Prozeduren. Klassen kommen also quasi "natürlicherweise" dort nicht vor, wo dieser Mehraufwand merkbar wäre. Das alles vorausgesetzt, es wäre keine Option, wie ich vorschlug.
Normalerweise bin ich doch hier der kleinliche Optimierungs-Eumel :)

guinnes 19. Nov 2010 07:26

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von Medium (Beitrag 1062466)
Normalerweise bin ich doch hier der kleinliche Optimierungs-Eumel :)

Wenn du auf einer Maschine mit 32KByte die Grundzüge der Programmierung gelernt hast, prägt dich das dein restliches Leben :)

alzaimar 19. Nov 2010 09:01

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
Zitat:

Zitat von guinnes (Beitrag 1062493)
Wenn du auf einer Maschine mit 32KByte die Grundzüge der Programmierung gelernt hast, prägt dich das dein restliches Leben :)

Was für ein Luxus. :stupid:

So ganz verstehe ich die Diskussion nicht. Eine Methode ohne jeglichen Bezug zum Objekt wird über eine ungütlige Instanz aufgerufen und richtet keinen Schaden an.
Wo ist der Fehler?
Nirgens*. Es funktioniert, es schadet nicht, hat keine Seiteneffekt usw.

*Natürlich ist da ein Fehler. Der sitzt vor dem Rechner und bemerkt es nicht einmal: Eine Methode ohne Bezug zum konkreten Objekt ist eine Klassenmethode.

Zitat:

Zitat von shmia (Beitrag 1062425)
...Was jetzt noch fehlt wäre ein Self-Pointer-Check.

Den gibt es schon und nennt sich FastMM.
Zitat:

Zitat von himitsu (Beitrag 1062430)
Du könntest ja einfach in alle Methoden dieses Einfügen.
Delphi-Quellcode:
Assert(Assigned(Self) and (TObject(Self) is TKlassentyp));
.

Nun ja. Hilft das bei bereits freigegebenen Instanzen?

Wenn man jeden Deppenfehler (und die Verwendung einer ungültigen Instanz ist so einer) mit Compilerschaltern ausmerzen könnte, wozu dann noch Programmieren lernen? Wieso entwickelt man nicht gleich einen Compiler, der nicht nur einen Syntaxcheck, sondern auch einen Semantikcheck durchführt?

Gegen Fehler dieser Art empfehle (nicht nur) ich das Einschalten des Switches "Self.Brain" sowie die Einhaltung elementarer Grundregeln. Dann passiert sowas einfach nicht.

Medium 19. Nov 2010 16:02

AW: Klassen-Funktion benutzen, ohne die Klasse zu erstellen
 
So argumentiert könnte man problemlos die Abschaffung moderner IDEs incl. all der netten Helferlein begründen, und mit Notepad schreiben, und den Compiler alles fressen lassen - egal was an Maschinencode dabei nachher raus kommt. Warum sich nicht helfen lassen, wo es möglich ist? Zumindest zur Entwicklungszeit. Zudem sehe ich wenig Zusammenhang zwischen "Programmieren können", und die richtigen Buchstaben so hinschreiben, dass es kompiliert und zur Runtime nicht knallt. Für gewöhnlich denkt der Informatiker in abstrakteren Ebenen, und ist froh, wenn ihm bei so blöden (gern auch aus Flüchtigkeit entstandenen) Patzern auf die Füße getreten wird.
Ich komme ehrlich gesagt nicht ganz dahinter, warum man sich gegen eine solche Option aussprechen wöllte :gruebel:. Ist Bergsteigen mit Seil jetzt auch uncool? ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz