Autor Beitrag
Symbroson
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: 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. :nixweiss:

Beispiel:

ausblenden 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);        //okay
    //v1.add(TVec3(7,8,9)); //memory-Leak
    //edit:
    v1.add(TVec3.Create(2,3,4)); //memory-Leak
    v1.free;
end;


Der Anfang der Vektor-Klasse sieht in etwa so aus:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 63
Erhaltene Danke: 27

Win95 - Win11 / MSServer2000 - MSServer2019
Delphi 6pro / XE4
BeitragVerfasst: 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



BeitragVerfasst: Mo 23.04.18 17:12 
- Nachträglich durch die Entwickler-Ecke gelöscht -

Für diesen Beitrag haben gedankt: Symbroson
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Mo 23.04.18 17:36 
Wow - vielen Dank! - das hilft mir sehr weiter. :zustimm: ich glaube, ich hätte sehr lange gebraucht, um das so hinzubekommen!

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden 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);        //okay
    v1.add(TVec3(7,8,9)); //memory-Leak
    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)(123).
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



BeitragVerfasst: Mo 23.04.18 18:10 
- Nachträglich durch die Entwickler-Ecke gelöscht -

Für diesen Beitrag haben gedankt: Symbroson
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Mo 23.04.18 18:20 
Achso dann ist das also soetwas wie bei Canvas.Pixels oder Scanline :idea:

Interessantes Konzept - zumindest flaggt jetzt MM4 nicht mehr :zustimm:

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



BeitragVerfasst: Mo 23.04.18 19:11 
- Nachträglich durch die Entwickler-Ecke gelöscht -

Für diesen Beitrag haben gedankt: Symbroson
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: 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:
ausblenden 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



BeitragVerfasst: Mo 23.04.18 22:25 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: 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



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: 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 :lol:

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



BeitragVerfasst: Fr 27.04.18 19:37 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: 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:
ausblenden volle Höhe 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:
program MatrixTest;
          
{$APPTYPE CONSOLE}

uses
    //FastMM4,
    SysUtils,
    //System, 
    //Dialogs, 
    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); //nicht benötigt, wenn in extra prozedur verwendet
    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



BeitragVerfasst: Fr 27.04.18 20:36 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: 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



BeitragVerfasst: Fr 27.04.18 20:55 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 382
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Fr 27.04.18 21:00 
Das hier erzeugt trotzdem eine Warnung:

ausblenden 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, {System,{} {Dialogs,{} 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]));
    
        //v1.add(v2.copy.mul(4).add(Vec3(5,3,-2))).copy;     
        writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs]));

    finally
        TVec3.Release(v1);
        //TVec3.Release(v2); // not required when used in extra procedure
    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)