Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Kapselung von Klassenprozeduren
Delphi-Laie - Di 23.10.12 16:22
Titel: Kapselung von Klassenprozeduren
Hallo Delphifreunde!
Derzeit bin ich dabei, eine weitere Langzahlenbibliothek (uBigIntsV3 aus der DFFLibV14) in meinen
Langzahlentaschnerechner [
http://www.entwickler-ecke.de/viewtopic.php?t=109667&highlight=langzahlentaschenrechner] zu integrieren.
Die Rechenfunktionen werden dort als Klassenprozeduren bereitgestellt, z.B.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| type TDigits = array of int64;
TInteger = class(TObject) protected Sign: integer; fDigits: TDigits; Base: integer; . . . public procedure Add(const I2: TInteger); overload; |
.
Zur flexiblen Handhabung - und auch zur Verschachtelung bei komplexeren Berechnungen - benötige ich jedoch Funktionen. Ein richtiges Ergebnis erhalte ich z.B. mit
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| function TI_Add(const i1,i2:TInteger):TInteger; begin result:=i1; result.add(i2) end; |
, aufgerufen z.B. so:
Delphi-Quelltext
1:
| Num3:=TI_add(Num1,Num2) |
Das Fatale: Sogar, wenn man beide Argumente in der Zielfunktion TI_Add als konstant deklariert sind, wird das erste Argument (Num1) im Ergebnis dieses Funktionsaufrufes verändert, also auch aus der Positionen derjenigen übergeordneten Funktion, aus der diese Kapselfunktion aufgerufen wird! Das ist zum Mäusemelken! Ich finde dafür keine Erklärung, weil der dortige Datentyp TInteger keine "verzeigerte" Datenstruktur ist. Ein Pointer mag ja konstant bleiben, aber der Inhalt der Adresse, auf die er hinzeigt, nicht, und schon verändern sich Inhalte, die darauf zeigen, doch diesen Fall kann ich hier nicht erkennen.
Bisherige funktionierende Abhilfe: Ich übergebe statt der Langzahl des Typs "TInteger" einen String und konvertiere diesen erst in die Kapselfunktion:
Delphi-Quelltext
1: 2: 3: 4: 5:
| function TI_Add(i1:string;i2:TInteger):TInteger; begin result:=ConvertStringToTI(i1); result.add((i2)) end; |
, doch das ist unelegant und ganz sicher gerade bei größeren/längeren Zahlen auch erheblich geschwindigkeitsmindernd.
Bei MPA von
gammatester [
http://www.entwickler-ecke.de/user_Gammatester.html] bekam ich das gleiche Problem gelöst, hier jedoch bisher nicht.
Kennt jemand den Grund, warum die Argumente in der übergeordneten Ebene nicht konstant bleiben und ggf. einen eleganteren Weg, die aufrufenden Argumente unbehelligt zu lassen?
Danke im voraus!
Moderiert von
Narses: Topic aus Sonstiges (Delphi) verschoben am Di 23.10.2012 um 18:21
jaenicke - Di 23.10.12 16:36
Variablen vom Typ TInteger sind Pointer, denn das ist eine Klasse. Und eine Variable dieses Typs ist immer ein Zeiger auf ein Objekt von diesem Typ.
Hier eignen sich auch Records viel besser, da diese (in einigermaßen aktuellen Delphiversionen, ab Delphi 2006) mit Klassenoperatoren sehr viel komfortabler nutzbar sind. Zudem lieferst du ja sonst Objekte zurück, und das führt nur zu Speicherlecks. (Das kann man natürlich mit Interfaces vermeiden.)
Delphi-Laie - Di 23.10.12 20:18
Danke, Sebastian!
"Irgendwie" ahnte ich das ja schon anhand des Verhaltens. So wundert es mich jetzt nicht. Diese elenden Zeiger....ich mag sie bis heute nicht...schön schnell, aber man muß eben wissen bzw. aufpassen, welche Seiteneffekte sie auslösen.
Werde ich also meine "Bastellösung" als Notbehelf (langsam und unelegant) oder es mit Records versuchen. Eigentlich schade, denn die Prozeduren sind verdammt schnell, schneller noch, als mir gammatesters MPA schon in der Hinsicht positiv auffiel (und derzeitiger Spitzenreiter ist, von Hagen Redmanns geheimer Verschlußsache einmal abgesehen).
jaenicke - Mi 24.10.12 06:15
Delphi-Laie hat folgendes geschrieben : |
Diese elenden Zeiger....ich mag sie bis heute nicht...schön schnell, aber man muß eben wissen bzw. aufpassen, welche Seiteneffekte sie auslösen. |
Ohne würde aber vieles schlicht gar nicht funktionieren, du merkst nur nicht wo du die überall implizit nutzt.
Für eine Kopie eines Objekts musst du eben ein neues erstellen und die Daten übernehmen. Nur dass du dann eben Speicherlecks hast. Aber wenn du wie gesagt Interfaces benutzt, hast du das Problem nicht.
Aber mal ein Beispiel warum Records wirklich Sinn machen (Delphi 2006+ wie geschrieben):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| type TInteger = record Value: Integer; class operator Add(const Value1, Value2: TInteger): TInteger; class operator implicit(const Value: String): TInteger; class operator implicit(const Value: TInteger): String; class operator implicit(const Value: Integer): TInteger; end;
class operator TInteger.Add(const Value1, Value2: TInteger): TInteger; begin Result.Value := Value1.Value + Value2.Value; end;
class operator TInteger.implicit(const Value: String): TInteger; begin Result.Value := StrToInt(Value); end;
class operator TInteger.implicit(const Value: TInteger): String; begin Result := IntToStr(Value.Value); end;
class operator TInteger.implicit(const Value: Integer): TInteger; begin Result.Value := Value; end;
var a, b: TInteger; begin a := 5; b := '10'; ShowMessage(a + b); end; |
Delphi-Laie - Mi 24.10.12 08:45
Das ist hervorragend, große Klasse und tausend Dank!
Jetzt nur noch eine Frage: Was sind
jaenicke hat folgendes geschrieben : |
Interfaces |
?
Das Wort "interface" ist ja ein Schlüsselwort und hat erstmal überhaupt nichts mit Typen, Variablen o.ä. zu tun.
Gruß Delphi-Laie
jaenicke - Mi 24.10.12 09:28
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| type IInteger = interface procedure SetValue(const AValue: Integer); function GetValue: Integer; property Value: Integer read GetValue write SetValue; end;
TInteger = class(TInterfacedObject, IInteger) private procedure SetValue(const AValue: Integer); function GetValue: Integer; public class function Add(const AValue1, AValue2: IInteger): IInteger; constructor Create(const AValue: Integer); property Value: Integer read GetValue write SetValue; end;
...
class function TInteger.Add(const AValue1, AValue2: IInteger): IInteger; begin Result := TInteger.Create(AValue1.Value + AValue2.Value); end; |
Da du nun ein Interface als Ergebnis hast, wird das automatisch freigegeben, wenn es nicht mehr benutzt wird (wenn die Variable aus dem Scope läuft).
Delphi-Laie - Mi 24.10.12 12:54
Neben dem Druck auf den Dank-Knopf jetzt auch noch mal ein Dank extra! Hast eben Deine vielen hundert Danks nicht zufällig.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!