Probleme mit dynamischen Arrays (malloc und free)



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Ich weiß leider nicht, was du mit t-tor meinst, entschuldige.

    t-tor = d-tor mit Tippfehler. = destructor. Einen leeren hinschreiben ist wie sowas.


  • Mod

    @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    @SeppJ sagte in Probleme mit dynamischen Arrays (malloc und free):

    aber allgemein gilt leider Bücher von Jürgen Wolf sind zum Lernen ungeeignet.

    Okay, welches Buch oder welche Bücher sind denn zum Lernen geeignet?

    Diese Liste wird recht gut gepflegt:
    https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282



  • Ich wollte mich eben um die Referenzen bemühen, aber bin auf Probleme gestoßen.
    Gegeben bspw. meine Operatorenüberladungen:

    // operator overloading
    	Matrix operator- (); //unary
    	friend Matrix operator+ (Matrix m, Matrix n);
    	friend Matrix operator- (Matrix m, Matrix n);
    	friend Matrix operator* (Matrix m, Matrix n);
    	friend Matrix operator* (Matrix m, double d);
    	friend Matrix operator* (double d, Matrix m);
    

    So, wie sie gerade sind, kann ich mit

    Matrix h = Matrix(3, { 1, 1, 0, 2, -40, 5, 20, 6, -3 });
    Matrix g = Matrix(3, { 7, 5, 0, 81, -6, 8, -10, 0, 2 });
    

    problemlos Ausdrücke wie

    ((4 * h) * -(g * h)).print();
    

    nutzen.
    Aber schon wenn ich

    friend Matrix operator* (Matrix& m, Matrix& n);
    

    habe, gibt es Probleme, weil ich in vielen Situationen ja keine Objekte habe, von denen referenziert wird.
    Auch wenn ich im Konstruktor von Matrix den std::vector<double> referenziere, kann ich obige Deklaration mit { ... } nicht mehr nutzen.

    Wie wird das dann gehandhabt?

    @SeppJ sagte in Probleme mit dynamischen Arrays (malloc und free):

    Diese Liste wird recht gut gepflegt:
    https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282

    Danke, da stöbere ich mal die Tage. 🙂



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    weil ich in vielen Situationen ja keine Objekte habe, von denen referenziert wird.

    Bahnhof. Kompilierbares Minimalbeispiel oder das ist nie passiert.



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    gibt es Probleme

    Aha. Welche?

    Vermutlich würde const helfen.

    friend Matrix operator* (const Matrix& m, const Matrix& n);
    


  • @Swordfish

    Da ich Matrizen nutze, möchte ich sie in mathematischen Formeln verwenden.
    Das bedeutet, dass

    ((4 * h) * -(g * h))
    

    eine Matrix ergeben soll. Ich müsste sonst für jede Teilrechnung ein Objekt erstellen und das ist schwer suboptimal.



  • @manni66 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Aha. Welche?

    Der Compiler verweigert den Dienst, weil die Operationen nicht definiert seien.

    @manni66 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Vermutlich würde const helfen.

    Gerade getestet, aber bei

    Matrix operator* (const Matrix& m, const Matrix& n) { //... }
    

    lässt der Compiler diesen Aufruf nicht mehr zu:

    if (!(m.getColumns() == n.getRows())) { //...}
    

    Dann kann ich nicht mehr auf Klassenmethoden von m und n zugreifen.



  • @Rav1642 Du zeigen was Du geschrieben und erklären was für Probleme Du haben.



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    lässt der Compiler diesen Aufruf nicht mehr zu:
    if (!(m.getColumns() == n.getRows())) { //...}

    @SeppJ sagte in Probleme mit dynamischen Arrays (malloc und free):

    Guck dir auch das Thema 'const-correctness' an. So etwas wie getSize() sollte const sein. Das ist auch etwas, was du jetzt wahrscheinlich nicht unbedingt machen musst, aber du könntest später Probleme bekommen, wenn du es nicht tust. Und wenn du dann Probleme bekommst, musst du gleich sehr viel ändern

    @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Der Compiler verweigert den Dienst,

    Aha

    Matrix.cpp C6666: Dienst verweigert

    Fehlermeldung: Copy&paste



  • @Swordfish

    #include "Matrix.h"
    #include <iostream>
    
    int main() {
    	Matrix h = Matrix(3, { 1, 1, 0, 2, -40, 5, 20, 6, -3 });
    	Matrix g = Matrix(3, { 7, 5, 0, 81, -6, 8, -10, 0, 2 });
    	((4 * h) * -(g * h)).print();
    	
    	std::cin.get();
    	return 0;
    }
    

    mit

    	friend Matrix operator* (Matrix& m, Matrix& n);
    

    in Class Matrix in matrix.h bewirkt, dass der mittlere Multiplikationsoperator * bei

    ((4 * h) * -(g * h))
    

    rot unterstrichen ist.
    Der Compiler sagt dann:
    Kein "*"-Operator stimmt mit diesen Operanten überein.
    Operandentypen sind: Matrix * Matrix



  • @Rav1642 Hst du die Antwort von @manni66 gelesen? Eine bessere Antwort wirst du nicht mehr kriegen: const!



  • @manni66 sagte in Probleme mit dynamischen Arrays (malloc und free):

    @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Der Compiler verweigert den Dienst,

    Aha

    Da stand ja die Begründung hinter, das hat ja nichts mit copy und paste zu tun.

    Ich habe in den letzten paar Tagen einiges gelernt. Dass ich die große Menge an Vorschlägen und Hinweisen bis hier hin noch nicht vollständig durchgearbeitet und verstanden habe, liegt sicher nicht an fehlendem Wille.
    Ich bemühe mich, möglichst viel von dem umzusetzen, was mir hier geraten wird. Ich setze ja nicht nur Recherchen, Codeoptimierungen und Fehlerkorrektur um, sondern schreibe ja auch die Methodenkörper. Da muss ich meine Zeit, die ich am Tag für C++ nutzen kann, sinnvoll aufteilen und habe noch nicht alles auf dem Schirm, entschuldige.


  • Mod

    Ja, das ist die fehlende Const-correctness, die dich jetzt beißt. Bei einfachen Ausdrücken wärst du vielleicht ohne weg gekommen, aber hier werden temporäre Ausdrücke erzeugt, und der Compiler hält sich selber natürlich ganz strikt an const-correctness. Das heißt, die Const-heit der temporären Ausdrücke sorgt dafür, dass sie nicht zu der Signatur deiner Multiplikationsfunktion passen, die (fälschlich) zwei nicht-const werte erwartet.

    Nun ist der Zeitpunkt gekommen, vor dem ich gewarnt habe, an dem du sehr viel ändern musst. Denn so ein const ist ansteckend. Wenn du die Referenzen zu dem Multiplikationsoperator nun korrekt const machst, wird es gewiss sofort oder bald an anderen Stellen zu Problemen kommen, wo du auch const fälschlich weggelassen hast. Letztlich wirst du alles const-correct machen müssen. Solltest du aber sowieso. Const ist schließlich nicht dazu da, um dich zu ärgern, sondern ein Hilfsmittel, das dir hilft, Fehler zu finden.



  • @Jockelx sagte in Probleme mit dynamischen Arrays (malloc und free):

    Hst du die Antwort von @manni66 gelesen? Eine bessere Antwort wirst du nicht mehr kriegen: const!

    Klar, ich halte mich stets auf dem Laufenden! 🙂
    Ich sehe, dass da mehr zu tun ist und kann das erst heute Abend umsetzen.

    Aber warum ist const hier die Lösung?
    Als Argument erwarte ich ein Objekt, auf das ich referenzieren kann, aber bei

    ((4 * h) * -(g * h))
    

    sind g und h zwar Objekte, allerdings die Matrizen nicht, die im letzten Schritt multipliziert werden. Also übergebe ich der Funktion ja kein Objekt. Also kann ja auf nichts referenziert werden, oder nicht? Warum kann ich mit const dann auf etwas referenzieren, das man doch eigentlich nicht referenzieren kann?
    Das entzieht sich aktuell leider meinem Verständnis.

    Ich danke für die Antwort!


  • Mod

    Der Compiler kann (offiziell) nicht in deine Funktionen gucken. Der sieht nur die Signatur deiner Multiplikationsfunktion. Die nimmt nicht-const-Referenzen als Argument. Das heißt, potentiell könnte diese Funktion ihre Argumente ändern, im Sinne von:

    void foo(int &i) { i=4;}
    

    Jetzt willst du diese Funktion auf das temporäre Zwischenergebnis deiner Rechnung anwenden. Das wäre im obigen Beispiel ungefähr so wie

    foo(3 * 7);
    

    Und das wäre ja totaler Quatsch, da du nicht das Ergebnis von 3 * 7 auf 4 setzen kannst. Und da der Compiler nicht ausschließen kann, dass dein Multiplikationsoperator so etwas nicht tut, lässt er das nicht zu. Wäre im oberen Beispiel hingegen

    void foo (const int &i) {...}
    

    dann wüsste er ganz sicher, dass i nicht geändert wird, und er könnte foo sorgenlos auf konstante Ausdrücke anwenden.



  • Alle erzeugten Matrix-Instanzen sind Objekte und können referenziert werden (egal ob direkt oder als temporäre Ausdrücke).

    Der wichtige Unterschied bzgl. der Parameter Matrix und Matrix & (bzw. const Matrix &) ist, daß bei ersterem das ganze Matrix-Objekt kopiert wird, während bei der Übergabe mittels Referenz (oder auch mittels Zeiger) intern nur die Adresse des Objekts übergeben wird.



  • @Th69 Maximal verwirrende Antwort...
    Um value oder reference geht es gar nicht, sondern darum, dass man eben nicht temporäre Objekte an nicht-const Referenzen binden kann.



  • @SeppJ sagte in Probleme mit dynamischen Arrays (malloc und free):

    Und da der Compiler nicht ausschließen kann, dass dein Multiplikationsoperator so etwas nicht tut, lässt er das nicht zu.

    Danke für diese sehr verständliche Erklärung! Ich persönlich finde es sehr wichtig, dass ich verstehe, warum etwas so gemacht wird, wie es gemacht wird.
    C++ ist durchaus komplex, aber mit solchen Erklärungen wird das Verständnis stetig klarer.

    Wird das Ergebnis einer temporären Rechnung als Objekt gespeichert und zeigt die Referenz darauf? Eine Referenz zeigt ja auf eine Speicheradresse.
    Ein kleiner Test mit

    	int z = 5;
    	int u = 17;
    	const int &ref = (z * u);
    
    	std::cout << &z << std::endl;
    	std::cout << &u << std::endl;
    	std::cout << &ref << std::endl;
    

    ergibt drei verschiedene Adressen. Mir persönlich sagt es, dass bei einer temporären Rechnung ein temporäres Objekt gespeichert wird. Ist das so?

    @Th69 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Alle erzeugten Matrix-Instanzen sind Objekte und können referenziert werden (egal ob direkt oder als temporäre Ausdrücke).

    Also ja, danke. 🙂

    @Jockelx sagte in Probleme mit dynamischen Arrays (malloc und free):

    @Th69 Maximal verwirrende Antwort...

    Der zweite Absatz von Th69 ist mir mittlerweile sehr geläufig, so dass ich nur las, was ich schon wusste. Der erste Absatz von Th69 allerdings hilft meinem Verständnis ungemein.

    @Jockelx sagte in Probleme mit dynamischen Arrays (malloc und free):

    dass man eben nicht temporäre Objekte an nicht-const Referenzen binden kann

    Das hat SeppJ gut verständlich gemacht. Ich habe den Beitrag von Th69 als sinnvolle Ergänzung aufgefasst.
    Danke, dass du darauf achtest, dass der Lernprozess nicht durch Missverständnisse gefährdet wird. 🙂

    Wie gesagt, ich werde am Abend const-correctness recherchieren und mein Programm anpassen. Wenn ich fertig bin, werde ich dazu eine Rückmeldung abgeben.


  • Mod

    @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Wird das Ergebnis einer temporären Rechnung als Objekt gespeichert und zeigt die Referenz darauf? Eine Referenz zeigt ja auf eine Speicheradresse.
    Ein kleiner Test mit

    	int z = 5;
    	int u = 17;
    	const int &ref = (z * u);
    
    	std::cout << &z << std::endl;
    	std::cout << &u << std::endl;
    	std::cout << &ref << std::endl;
    

    ergibt drei verschiedene Adressen. Mir persönlich sagt es, dass bei einer temporären Rechnung ein temporäres Objekt gespeichert wird. Ist das so?

    Kann so sein, muss aber nicht. Die Regeln der Sprache sagen, dass sich das Programm so verhalten muss, als ob da ein temporäres Objekt erzeugt würde. Generell darf ein Compiler aber beliebigen Code erzeugt, solange er sich so verhält wie das Programm es vorschreibt (sogenannte "as-if" Regel ). Dabei kann es sehr gut vorkommen, dass so Konzepte wie "Variablen", "Objekte", "Klassen", etc. komplett verloren gehen. Denn auf Maschinenebene gibt es diese Konzepte gar nicht, das sind alles nur Hilfsmittel, um dem menschlichen Programmierer das Nachdenken über das Programmverhalten zu erleichtern.

    Deine Ausgabe der Adressen der Objekte in deinem Beispiel gehört zum beobachtbaren Verhalten des Programms. Das darf der Compiler dann nicht mehr einfach wegoptimieren. Da die Regeln der Sprache sagen, dass du das Ergebnis an eine Referenz binden darfst und du dessen Adresse nehmen darfst, zwingst du hier den Compiler, tatsächlich irgendwie Speicherplatz für ein Zwischenergebnis deiner Rechnung vorzusehen. Wenn du die Adresse nicht ausgeben würdest, hätte er höchstwahrscheinlich anderen Code erzeugt, bei dem das Rechenergebnis nur temporär in irgendeinem (unadressierbarem) Rechenregister stehen würde.

    Insgesamt bin ich überrascht, dass du auf genau diese Variante gekommen bist, da dies eine der ganz wenigen Möglichkeiten ist, ein temporäres Objekt am Leben zu erhalten und dessen Adresse zu beziehen. Die direkte Variante cout << &(z*u); würde beispielsweise nicht funktionieren.



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Bisher habe ich mich in das Thema der Rule of Five nicht einarbeiten können, wahrscheinlich auch, weil ich für meine Zwecke keinen Bezug finde.

    Überleg' mal, was passiert, wenn Du

    struct foo
    {
        int *bar;
        foo() : bar{ new int{} } {}
        ~foo() { delete bar; }
    };
    

    machst bei

    foo f;
    foo g{ f };
    g = f;
    

Anmelden zum Antworten