Autor |
Beitrag |
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 23.04.18 09:09
Hallo EE,
Ich bin gerade dabei eine Vektor- bzw. Matrix-Klasse für den Info-Unterricht zu schreiben. Leider gibt es ja kein Operator-Overloading bei Delphi7 Klassen, also behelfe ich mich mit add,mul,scalar,cross usw. Methoden, die als Argument einen anderen Vektor erhalten.
Wenn man allerdings einen Vektor nur temporär für eine Rechenoperation braucht müsste man sich ja erst ein Vektor-Objekt erstellen, und kann es später nicht wieder freigeben, weil man ja nicht ständig neue variablen dafür erstellen will.
Also habe ich die Methoden überladen um anstatt dem Vektor die einzelnen x,y,z-Werte zu erwarten.
Jetzt stellt sich mir die Frage, ob man evtl irgendwie erreichen kann, dass das Objekt später wieder freigegeben wird, wenn es keiner Variable zugewiesen wurde. oder irgendwie so.
Beispiel:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var v1 : TVec3; begin v1 := TVec3.Create(1,2,3); v1.add(4,5,6); v1.add(TVec3.Create(2,3,4)); v1.free; end; |
Der Anfang der Vektor-Klasse sieht in etwa so aus:
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:
| unit MatrixMath;
interface
uses FastMM4;
type PVec3 = ^TVec3; TVec3 = class x, y, z: double; constructor Create; overload; constructor Create(v: TVec3); overload; constructor Create(v: double); overload; constructor Create(x, y, z: double); overload;
function copy: TVec3; function add(v: TVec3): TVec3; overload; function add(v: double): TVec3; overload; function add(x, y, z: double): TVec3; overload;
|
Das FastMM4 nutze ich nur für die Memory-Leak-Erkennung.
Grüße,
Symbroson
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Zuletzt bearbeitet von Symbroson am Fr 27.04.18 20:14, insgesamt 2-mal bearbeitet
|
|
drstar
Beiträge: 79
Erhaltene Danke: 2
Windows 8.1/x64
Delphi 10.1
|
Verfasst: Mo 23.04.18 10:21
constructor mit overload halte ich für keine gute Idee. Um die Freigabe des Objekts kümmert sich destroy, deshalb kennzeichnet man auch beide mit inherited, um den ursprünglichen Konstruktor bzw. Destruktor aufzurufen. Um die Speicherverwaltung kümmert sich die Klasse dann allein, genau das ist ja der Sinn dahinter.
Um dann die Klasse (nicht das Objekt) wieder freizugeben, ruft man dann destroy auf, auch hier dann mit inherited, um nicht nur den ursprünglichen Destruktor aufzurufen, sondern auch alles "Selbstgemachte" mit zu entsorgen. Wichtig noch die Reihenfolge: Beim Konstruktor als erstes den Ursprungs-Konstruktor aufrufen und dann den eigenen abzuarbeiten, beim Destruktor erst alles Selbstgemachte beseitigen und dann den Ursprungs-Destruktor aufrufen. Dann sollte alles eigentlich kein Problem mit der Speicherverwaltung sein.
Für diesen Beitrag haben gedankt: Symbroson
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 23.04.18 11:06
Ok, also ich muss mir das nachmal anschauen wie das zu benutzen ist, sodass das so funktioniert wie ich mir das vorstelle ^^
Danke schonmal für deine Antwort
Kannst du mir vllt mein kleines Beispiel so erweitern, dass sich die Klasse (irgendwann, möglichst bald, vllt nach Funktions-/Prozedurende) selbst zerstört?
Sonst bräuchte ich ja irgendwie nen Garbage-Collector...
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Holgerx
Beiträge: 63
Erhaltene Danke: 27
Win95 - Win11 / MSServer2000 - MSServer2019
Delphi 6pro / XE4
|
Verfasst: Mo 23.04.18 11:12
Hmm..
Ich glaube, dass was Du haben willst geht mit 'normalen' Objecten nicht.
Hier werfe ich mal 'Interface' in den Raum, da diese eine Referenzzählung haben und freigegeben werden, wenn sie nicht mehr referenziert werden.
Für diesen Beitrag haben gedankt: Symbroson
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 23.04.18 17:12
- Nachträglich durch die Entwickler-Ecke gelöscht -
Für diesen Beitrag haben gedankt: Symbroson
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 23.04.18 17:36
Wow - vielen Dank! - das hilft mir sehr weiter. ich glaube, ich hätte sehr lange gebraucht, um das so hinzubekommen!
Frühlingsrolle hat folgendes geschrieben : | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| var v1 : TVec3; begin v1 := TVec3.Create(1,2,3); v1.add(4,5,6); v1.add(TVec3(7,8,9)); v1.free; end; |
Das kann garnicht funktionieren, da du an jener Stelle den Klammerausdruck direkt castest. In C++ würdest du das auch nicht machen: (TVec3)(1, 2, 3).
|
Entschuldigung, es hätte heißen müssen v1.add(TVec3.Create(2,3,4));
Zitat: | Und ich hab's dir schonmal gesagt, verwende einen try-finally Block, wenn du Objekte freigeben musst! |
ja 'tschuldigung - ich hätte es später noch hinzugefügt
noch eine Frage: Was genau bewirkt das property? Machen die Get bzw Set funktionen es nicht komplizierter? Oder wofür sind die sonst da?
[Edit:] ach, die sind bestimmt inherited-spezifisch. Das macht Sinn ^^
LG
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 23.04.18 18:10
- Nachträglich durch die Entwickler-Ecke gelöscht -
Für diesen Beitrag haben gedankt: Symbroson
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 23.04.18 18:20
Achso dann ist das also soetwas wie bei Canvas.Pixels oder Scanline
Interessantes Konzept - zumindest flaggt jetzt MM4 nicht mehr
Es sieht auch so aus, als müsste ich für TVec3 und IVec3 jeweils weitere overloads erstellen...
[Edit:] Könnte Ich nicht anstatt der Vec3- class nur das Vec3- interface nehmen und dort alle Methoden hineinpacken? Das macht doch im Endeffekt keinen Unterschied, oder?
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 23.04.18 19:11
- Nachträglich durch die Entwickler-Ecke gelöscht -
Für diesen Beitrag haben gedankt: Symbroson
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 23.04.18 20:38
Geht das irgendwie, dass ich eine allgemeingültige Free bzw Release-funktion sowohl für IVec3 als auch TVec3 habe? Sonst muss ich beide Objekte jeweils unterschiedlich freigeben (mit := nil bzw Free) - und das kann dann doch eher verwirrend sein...
Alles, was ich bisher versucht habe, hat nicht funktioniert. Ich habe einiges mit try..catch blöcken und _Release <= 0 bzw Free oder FreeAndNil() versucht, hat aber alles irgendwie nicht so ganz geklappt - meistens kam eine EInvalidPointer Exception oder ähnliches.
es sollte am Ende in etwa so aussehen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| var v1 : TVec3; v2 : IVec3; begin v1 := TVec3.Create(2,3,5); v2 := Vec3(1,4,6); v2.x := v2.x + 9.4; writeln('1: (' + floattostr(v1.x) + '|' + floattostr(v1.y) + '|' + floattostr(v1.z) + ')'); v1.add(v2.copy.mul(4)); writeln('2: (' + floattostr(v1.x) + '|' + floattostr(v1.y) + '|' + floattostr(v1.z) + ')'); v1.Release; v2.Release; readln; end. |
Danke vielmals
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 23.04.18 22:25
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Di 24.04.18 05:11
copy gibt eine Kopie des Objektes/Interfaces zurück und mul() multipliziert die Werte der Vektoren bzw streckt/skaliert ihn mit einem Wert. Anders als scalar() für Skalarprodukte bzw cross() für Kreuzprodukte. Später werden mit mul() auch Multiplikationen mit einer 3×3 Matrix möglich.
Ich versuche eigentlich so etwas wie die glm Bibliothek von c++ zu schaffen - nur will ich alles auch mal selber gemacht haben anstatt immer nur fertige Bibliotheken zu nutzen - und wo das nun in Info schonmal dran is bietet sich ja dazu die Gelegenheit.
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Fr 27.04.18 11:57
- Nachträglich durch die Entwickler-Ecke gelöscht -
Zuletzt bearbeitet von Frühlingsrolle am Sa 28.04.18 12:54, insgesamt 1-mal bearbeitet
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Fr 27.04.18 13:56
Das wäre wirklich nicht nötig gewesen - die Klasse selbst hab ich schon selber gebaut.
Ich habe ja auch schon selber mit glm in C++ OpenGL gearbeitet - ich bin mir dem Umpfang natürlich bewusst
Es kommen aber trotzdem noch Matritzen (3×3 und 4×4 für den Raum) dazu, sowie zumindest 2er Vektoren. Fang jetzt aber bloß nicht an das auch zu machen
Das copy hab ich hinzugefügt für den Fall, dass ich mit dem Wert einer Variable rechnen, aber nicht die Variable selbst verändern möchte - zB. für die ModelView Matrix für alle Vertices - die sollen ja nicht die Position des Vertex verändern.
Ich überlege auch noch, ob ich nicht vielleicht doch eher class functions/ procedures verwende wie auch glm - Ich weiß ja nicht wie Delphi das dann handlet - ob je Funktion pro Kind ein extra Funktionszeiger angelegt werden muss, oder eben nicht. Bei Klassenfunktionen sollte das ja in jedem Fall nicht so sein.
Danke nochmal für deine Hilfe ^^
[Edit] Frage: warum legst du immer eine extra Variable an, anstatt direkt mit result zu arbeiten? Es gibt ja schon extra result... warum es nicht ausnutzen? (zumindest mache ich das meistens)
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Zuletzt bearbeitet von Symbroson am Sa 28.04.18 09:03, insgesamt 1-mal bearbeitet
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Fr 27.04.18 19:37
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Fr 27.04.18 19:40
Ja, das ist mir auch aufgefallen. Dafür hab ich jetzt eine Release fnkt die für beide in den meisten Fällen funktioniert.
Ich hab mal meine aktuelle und fast finale Vec3 Klasse in den Anhang gepackt. Das mit dem IVec3 als Rückgabe versuche ich mal - das würde aber die Nutzung von IVec3 erzwingen, da TVec3 damit nicht kompatibel ist. (umgekehrt aber irgendwie schon...)
Bsp:
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:
| program MatrixTest; {$APPTYPE CONSOLE}
uses SysUtils, UVec3 in 'glm/UVec3.pas';
var v1 : TVec3; v2 : IVec3; begin try v1 := TVec3.Create(2,3,5); v2 := Vec3(1,4,6); v2.x := v2.x + 9.4; writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs])); v2 := v1.add(v2.copy.mul(4)).copy; writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs]));
finally TVec3.Release(v1); TVec3.Release(v2); end; readln; end. |
Einloggen, um Attachments anzusehen!
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Fr 27.04.18 20:36
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Fr 27.04.18 20:44
Aus irgendeinem Grund zeigt FastMM4 aber auch beim interface eine Warnung an, wenn ich es in dem Konsolenanwendungs-begin end. definiere. Deswegen das Release
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Fr 27.04.18 20:55
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Fr 27.04.18 21:00
Das hier erzeugt trotzdem eine Warnung:
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:
| program MatrixTest; {$APPTYPE CONSOLE}
uses FastMM4, SysUtils, UVec3 in 'glm/UVec3.pas';
var v1 : TVec3; v2 : IVec3; begin v1 := TVec3.Create(2,3,5); v2 := Vec3(1,4,6); try v2.x := v2.x + 9.4; writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs])); writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs]));
finally TVec3.Release(v1); end; readln; end. |
Auch ganz ohne Befehle im try-Block kommt die Warnung.
Meine Theorie ist, dass entweder FastMM4 es nicht mitbekommt, also zu früh auswertet, oder dass Delphi zu spät merkt, dass v2 keine Referenzen mehr hat und nicht mehr freigibt.
Meistens verwendet man das ja eh nicht in einer Konsolenanwendung, sondern in ner Form oder zumindest in einer extra Funktion - dann hat sich das eh erledigt.
Im Zweifelsfall legt man sich einfach eine main-Prozedur an, in der man das ganze Zeug aus begin end. hineinpackt.
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
|