Methode aus Basisklasse aufrufen.



  • Hab noch zwei Fragen zu meiner Klasse:

    #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)
    {
        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)
    {
        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";
        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, werner;
    
        return 0;
    }
    

    1.
    Das was ich in "main" mache, Zeile 84, dem Objekt sein "Rating1" Teil raußreisen, und es durch eine Kopie von "Rating1" aus dem Objekt "hans" ersetze, ist das korrekt oder gibts da irgendwo Speicherverletzungen?

    2.
    Warum kann ich in Zeile 84 anstatt:

    werner=hans->Clone();
    

    nicht:

    werner(hans->Clone());
    

    machen? Wenn ichs so mache kommt eine Fehlermeldung:

    Compiling: C:\MinGW\Andi\buch.cpp
    C:\MinGW\Andi\buch.cpp: In function int main()': C:\\MinGW\\Andi\\buch.cpp:84: error:werner' cannot be used as a function
    Process terminated with status 1 (0 minutes, 0 seconds)
    1 errors, 0 warnings

    Dankeschön schon mal im Voraus.
    Stromberg

    PS: (Wenn sich jemand aufregt :D) Ich hab keinen dynamic cast verwendet, weil das erst in meinem Buch im nächsten Kapitel vorkommt. Aber in dem Kapitel bin ich leider noch net.



  • Stromberg schrieb:

    Das was ich in "main" mache, Zeile 84, dem Objekt sein "Rating1" Teil raußreisen, und es durch eine Kopie von "Rating1" aus dem Objekt "hans" ersetze, ist das korrekt oder gibts da irgendwo Speicherverletzungen?

    Das nicht, aber ein Speicherleck gibt's, da du das alte Rating1-Objekt nie freigibst. Du solltest dir entweder std::auto_ptr ansehen (das kann aber warten) oder du löschst das Objekt anständig, bevor du den Zeiger überschreibst. C++ hat von Haus aus keinen Müllschlucker.

    Stromberg schrieb:

    Warum kann ich in Zeile 84 anstatt:

    werner=hans->Clone();
    

    nicht:

    werner(hans->Clone());
    

    machen?

    Weil es nicht dasselbe ist. Ersteres ist eine Zuweisung, letzteres eine Funktionsanwendung. Da „werner“ aber weder eine Funktion noch ein Funktionszeiger noch ein Funktor ist bekommst du einen Fehler.

    Du verwechselst hier mit einem Konstruktoraufruf (der ja beide Formen haben kann, aber etwas völlig anderes ist!) und Zuweisung.

    Stromberg schrieb:

    delete hans, werner;
    

    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.

    Generell solltest du dir mehr Gedanken darüber machen, welcher Zeiger worauf zeigt. Du siehst ja, mit wie wenig Code man Speicherlecks produzieren kann. (Und keine Sorge, später wirst du das auch automatisieren können ;))



  • Du brauchst auch einen richtigen Copy-Konstruktor und op=, weil die Member (int-pointer) sonst bei mehreren Objekten auf das gleiche zeigen. Beim delete krachts dann, weil mehrmals versucht wird den gleichen Speicher freizugeben.



  • @Airdamn
    Ich kann dir nicht ganz folgen, ich hab doch Kopierkonstruktoren, und diese schaune doch auch extra drau das neuer Speicher reserviert word, und sich keine zwei Objekte eine Adresse teilen....oder was meinst du?

    @.filmor
    Tut mir leid, aber ich verstehe nicht so ganz, meinst du das man nicht:

    delete hans, werner;
    

    schreiben sollte, sondern:

    delete hans;
    delete werner;
    

    ?

    EDIT: Den letzteren Teil hab ich shcon wieder gelöscht, weil das hab ich jetzt schon wieder verstanden, war unsinn was ich da geschrieben habe...:D

    Dankeschön schon mal im Voraus.



  • Äh und noch was kurz, hier mal noch der Code, mit der Erweiterung das die Kopierkonstruktor sich jetzt melden wenn se eingesetzte werden:

    #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, werner;
    
        return 0;
    }
    

    AUSGABE:

    BASIS KLASSE: 18 37
    ABGELEITETE KLASSE: 23 41

    BASIS KLASSE: 33 44
    ABGELEITETE KLASSE: 2 3

    CopK Com
    CopK Rat
    BASIS KLASSE: 18 37
    ABGELEITETE KLASSE: 23 41

    So als erste steht da ja jetzt "CopK Com" mhhh? versteh ich jetzt mal gar nicht. Also jetzt geh ich den Ablauf mal durch:
    Mit dem Befehl hier gehts los

    werner=hans->Clone();
    

    so, dann wird im "Klasse Computer" Teil, "Clone()" aufgerufen, dieser aufruf wird an den "Klasse Rating1" Teil weiter geschickt, da die Methode dort redefiniert ist.
    Dort wird dann neuer Speicher reserviert, und gleich ein Objekt nämlich "*this" übergeben.

    return new Rating1(*this);
    

    Dadurch wird im soeben neu erstellten Objekt "werner" der Kopierkonstruktor aufgerufen, da ja ein ganze Objekt übergeben wird. Und dann sollte somit also der Kopierkonstrutktor vom "Computer" teil aufgerufen werden. So weiter weiß ich nicht mehr. Mh...verdammt! Warum wird dann eigentlich der Kopierkonstruktor vom "Rating1" Teil aufgerufen...mh schon klar, der muss schon aufgerufen werden...aber nach meiner Logigk kann der gar nicht aufgerufen werden....da Kopierkostruktoren ja nicht virtuell sein können, also sollte doch nach dem Aufruf vom "Computer" Kopierkonstrutor Schluss sein?...MHHH!!!
    Kann mir jemand bitte diesen Ablauf mal genau erklären?

    Dankeschön schon mal im Voraus.



  • Hat niemand eine Antwort parat?
    MfG
    Stromberg



  • Stromberg schrieb:

    So als erste steht da ja jetzt "CopK Com" mhhh? versteh ich jetzt mal gar nicht. Also jetzt geh ich den Ablauf mal durch:
    Mit dem Befehl hier gehts los

    werner=hans->Clone();
    

    so, dann wird im "Klasse Computer" Teil, "Clone()" aufgerufen, dieser aufruf wird an den "Klasse Rating1" Teil weiter geschickt, da die Methode dort redefiniert ist.
    Dort wird dann neuer Speicher reserviert, und gleich ein Objekt nämlich "*this" übergeben.

    return new Rating1(*this);
    

    Dadurch wird im soeben neu erstellten Objekt "werner" der Kopierkonstruktor aufgerufen, da ja ein ganze Objekt übergeben wird. Und dann sollte somit also der Kopierkonstrutktor vom "Computer" teil aufgerufen werden. So weiter weiß ich nicht mehr. Mh...verdammt! Warum wird dann eigentlich der Kopierkonstruktor vom "Rating1" Teil aufgerufen...mh schon klar, der muss schon aufgerufen werden...aber nach meiner Logigk kann der gar nicht aufgerufen werden....da Kopierkostruktoren ja nicht virtuell sein können, also sollte doch nach dem Aufruf vom "Computer" Kopierkonstrutor Schluss sein?...MHHH!!!
    Kann mir jemand bitte diesen Ablauf mal genau erklären?

    Ich versuchs mal, auch wenn ich die Frage jetzt nicht ganz verstanden hab 😃

    Also, du rufst new Rating1(*this) auf. Deine Feststellung "Dadurch wird im soeben neu erstellten Objekt "werner" der Kopierkonstruktor aufgerufen" ist aber leider falsch :p An dieser Stelle ist ja der Zeiger "werner" aus der main-Funktion noch unbelegt. Es wird also erstmal Speicherplatz reserviert und der Kopierkonstruktor von Rating1 aufgerufen. Haste nämlich Recht, Konstruktoren können nicht virtuell sein, deshalb wird immer von der Klasse der Konstruktor aufgerufen, die bei "new" dabeisteht 🙂 So, jo, dann müsste ja eigentlich alles klar sein, oder? 🤡



  • 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?


Anmelden zum Antworten