Methode aus Basisklasse aufrufen.



  • Mh nein, leider nicht 😞 .
    Weil bei der Ausgabe aus der Konsole sieht man doch, das zwei Kopierkosntruktoren aufgerufen werden, und der vom "Computer Teil", wird auch noch zuerst aufgerufen:

    BASIS KLASSE: 18 37
    ABGELEITETE KLASSE: 23 41

    BASIS KLASSE: 33 44
    ABGELEITETE KLASSE: 2 3

    CopK Com // <-- Kopierkonstruktor "Computer Teil"
    CopK Rat // <-- Kopierkonstruktor "Rating1 Teil"
    BASIS KLASSE: 18 37
    ABGELEITETE KLASSE: 23 41

    Mhhhh?

    MfG
    Stromberg



  • Oh, right 🤡

    Rating1::Rating1(const Rating1 &rhs)  // Der Kopier-Konstruktor von Rating1
    :Computer(rhs)                        // <- hier rufst doch selber den Kopier-Konstruktor von Computer auf
    {
        cout << "CopK Rat\n";
        m_m1=new int(rhs.get_m1());
        m_m2=new int(rhs.get_m2());
    }
    

    merkste? hm? hm? hm? 🙂



  • AHHHH!!!! MHMHMHM!! KAPISCHE 😃



  • okay, dann wäre da noch das mit dem was ".filmor vorhin gesagt hat:

    .filmor schrieb:

    Ich bin mir nicht ganz sicher, aber wenn ich Recht habe ist das dein zweites Speicherleck in diesem Programm, da „hans“ nicht freigegeben wird.

    Der Grund dass das nicht so ist ist, dass das Standardverhalten des Kommaoperators ist, alle Ausdrücke auszuwerten, aber nur den letzten zurückzugeben. Das heißt, hier wird genau einmal der Speicher freigegeben.

    Meine Frage dazu war:
    [quote="Stromberg"]
    Tut mir leid, aber ich verstehe nicht so ganz, meinst du das man nicht:

    delete hans, werner;
    

    schreiben sollte, sondern:

    delete hans;
    delete werner;
    

    ???

    Soll ich dann in Zukunft nachm "delete" nur immer eine Sache angeben? Weil in Zeile 38 und Zeile 73 hab ich das ja auch so mit "," komma gemacht.

    Sieht sonst noch irgendjemand eine Speicherverletzung?

    #include <iostream>
    using namespace std;
    
    class Computer
    {
        public:
        Computer(int a_, int b_);
        Computer(const Computer &rhs);
        virtual ~Computer();
        inline int get_a() const {return *m_a;}
        inline int get_b() const {return *m_b;}
        virtual Computer * Clone() const {return new Computer(*this);}
        virtual inline int get_m1() const {return 0;}
        virtual inline int get_m2() const {return 0;}
    
        private:
        int *m_a, *m_b;
    };
    
    Computer::Computer(int a_, int b_)
    {
        m_a=new int(a_);
        m_b=new int(b_);
    }
    
    Computer::Computer(const Computer &rhs)
    {
        cout << "CopK Com\n";
        m_a=new int(rhs.get_a());
        m_b=new int(rhs.get_b());
    
    }
    
    Computer::~Computer()
    {
        delete m_a, m_b;
    }
    
    class Rating1 : public Computer
    {
        public:
        Rating1(int a_, int b_, int m1_, int m2_);
        Rating1(const Rating1 &rhs);
        ~Rating1();
        inline int get_m1() const {return *m_m1;}
        inline int get_m2() const {return *m_m2;}
        Rating1 * Clone() const {return new Rating1(*this);}
    
        private:
        int *m_m1, *m_m2;
    };
    
    Rating1::Rating1(int a_, int b_, int m1_, int m2_)
    :Computer(a_,b_)
    {
        m_m1=new int(m1_);
        m_m2=new int(m2_);
    }
    
    Rating1::Rating1(const Rating1 &rhs)
    :Computer(rhs)
    {
        cout << "CopK Rat\n";
        m_m1=new int(rhs.get_m1());
        m_m2=new int(rhs.get_m2());
    }
    
    Rating1::~Rating1()
    {
        delete m_m1, m_m2;
    }
    
    int main()
    {
        Computer *hans=new Rating1(18,37,23,41);
        cout << "BASIS KLASSE: " << hans->get_a() << " " << hans->get_b() << "\n";
        cout << "ABGELEITETE KLASSE: " << hans->get_m1() << " " << hans->get_m2() << "\n\n";
        Computer *werner=new Rating1(33,44,2,3);
        cout << "BASIS KLASSE: " << werner->get_a() << " " << werner->get_b() << "\n";
        cout << "ABGELEITETE KLASSE: " << werner->get_m1() << " " << werner->get_m2() << "\n\n";
        delete werner;
        werner=NULL;
        werner=hans->Clone();
        cout << "BASIS KLASSE: " << werner->get_a() << " " << werner->get_b() << "\n";
        cout << "ABGELEITETE KLASSE: " << werner->get_m1() << " " << werner->get_m2() << "\n\n";
    
        delete hans;
        delete werner;
    
        return 0;
    }
    

    Dankeschön schon mal im Voraus.



  • Probiers doch einfach aus (selbst ist der Mann) 🙂

    class Test
    {
    	public:
    		Test() { cout << "Test()" << endl; }
    		~Test() { cout << "~Test()" << endl; }
    };
    
    int main(int argc, char *argv[])
    {
    	Test* t1 = new Test();
    	Test* t2 = new Test();
    	delete t1, t2;
    	return 0;
    }
    

    Und Speicherlecks sehe ich sonst keine. Nur könntest du m_a, m_b usw eigentlich auch als nicht-Zeiger implementieren, also einfach "int m_a". Dann brauchst du nicht jedesmal die 4 Bytes an Speicherplatz reservieren, oder hast du durch die Zeiger einen bestimmten Nutzen?



  • Nä, die sind halt nur Übunghalber, das es bisschen schwieriger ist, und ich was gescheites in meine Kopierkonstruktoren reinschreiben kann. Mh du nutzt ja auch:

    delete t1, t2;
    

    , mh dann mach ich das in Zukunft auch so! Was hat ".filmor" dann bloß vorhin gemeint?

    MfG
    Stromberg



  • Moooooment! Ich hab nich gesagt, dass ich das auch nutze.. Ich wollte dir quasi nur vorschlagen, den Codeschnipsel mal durch den Compiler zu scheuchen - dann wäre dir nämlich aufgefallen, dass nur ein Destruktor aufgerufen wird 😉
    Mit einfach Worten: delete a, b geht nicht 🤡



  • Aha, okay. Is aber eigentlich blöd gemacht, das man immer n neues "delete" machen muss. Das hätt sich der Erfinder mal besser überlegen sollen! Und des dümste is ja eigentlich, warum gibts dann kein Compilier Fehler? Weil des is doch einfach falscher Syntax oder?

    MfG
    Stromberg



  • Stromberg schrieb:

    Weil des is doch einfach falscher Syntax oder?

    Nein, das ist der Komma-Operator. Und wenn du den nicht kennst, ist das nicht die Schuld des "Erfinders".



  • Ja das verwirrt einen halt, weil sowas ist ja erlaubt:

    int *m_a, *m_b;
    


  • Das ist das selbe Problem wie das Verwechseln von Konstruktor- und Zuweisungsoperatoraufruf. Es gibt einerseits Deklaration und Definitionen und andererseits Ausdrücke.



  • MFK schrieb:

    Nein, das ist der Komma-Operator. Und wenn du den nicht kennst, ist das nicht die Schuld des "Erfinders".

    Sicher nicht die Schuld des Erfinders, aber besonders häufig wird er ja nicht erwähnt, erst recht nicht in kleineren Ich-lerne-C++-Büchern 🙂



  • Wo kann ich was über dne sog. "Komma Operator" finden?



  • Eigentlich gibt's über den recht wenig zu sagen. Er steht sehr weit unten in der Rangfolge (also um ihn sicher anzuwenden (zb in Funktionsaufrufen) musst du schon Klammern setzen), er kann überladen werden (was man aber aufgrund der Verwechslungsgefahr recht selten tut, und wenn, dann nur mit einem deutlichen Hinweis) und sein Standardverhalten ist, wie oben beschrieben.

    Im Prinzip wird der Kommaoperator auch immer nur auf zwei Elemente angewendet, also entspricht a, b, c dann ((a, b), c), was wiederum operator,(operator,(a, b), c) entspricht. Es werden beide Parameter ausgewertet und der Wert des rechten zurückgegeben.


  • Mod

    .filmor schrieb:

    Im Prinzip wird der Kommaoperator auch immer nur auf zwei Elemente angewendet, also entspricht a, b, c dann ((a, b), c), was wiederum operator,(operator,(a, b), c) entspricht. Es werden beide Parameter ausgewertet und der Wert des rechten zurückgegeben.

    Es muss unbedingt dazu gesagt werden, dass der Kommaoperator ebenso wie die Kurzschlußoperatoren && und || zunächst den linken Operanden auswertet und alle Seiteneffekte (abgesehen von der Zerstörung temporärer Objekte) abgeschlossen sind, bevor der rechte Operand ausgewertet wird. Er ist also ein "sequence point". Weil diese Eigenschaft beim Überladen verloren geht, sollte er in der Regel nicht überladen werden.

    P.S. Der Komma Operator steht nicht nur "sehr weit unten" sondern ganz unten in der Hierarchie - er hat die niedrigste Priorität aller Operatoren in C++

    P.P.S. Im Gegensatz zu den logischen Operatoren ist es leicht, um überladene Kommaoperatoren herumzucoden, wenn das notwendig ist: Es genügt, den linken Operanden nach void zu casten. Ein Ausdruck vom Typ void kann niemals Argument eines Funktionsaufrufes sein, folglich auch niemals den Aufruf eines überladenen Operators bewirken.



  • Aaaaaaah, deswegen der cast auf void in so vielen Makros 🙂
    Man kann damit aber wirklich ein paar ziemlich perverse Sachen machen:

    int* foo()
    {
    	int *a = new int(1), *b = new int(2);
    	return delete a, b;
    }
    


  • Also wenn ich soetwas mach, mache ich der übersichtlichkeit halber ne klammer rum, aber das ist doch noch keine über/fehlnutztung des kommaoperators. Man kann ihn da benutzten, wo man zu faul ist {klammern} zu öffnen, ...



  • Oder dort, wo man es nicht kann. Zb in einem for.


Anmelden zum Antworten