Vererbung und Überschreiben von STDOUT



  • Hallo Leute !!

    Ich versuch mich grad ein bisschen an der Vererbung und hänge bei folgendem Problem:

    Ich habe 1 Klasse TestClassA und 2 Klassen die davon erben TestClassB und TestClassC.

    Für die verbesserte Ausgabe habe ich jeweils STDOUT überschrieben

    In TestClassA hab ich 2 Variablen und in den Unterklassen jeweils eine weitere Variable damit die Vererbung auch halbwegs sinnvoll ist:

    #include <iostream>
    
    TestClassA::TestClassA(int alter_in, int gewicht_in)
    {
    alter=alter_in;
    gewicht=gewicht_in;	
    }
    
    std::ostream & operator << ( std::ostream & stream , Fahrzeug const & data  ) 
    { 
      return stream <<data.alter<<" "<<data.gewicht;
    }
    

    Die vererbte Klasse sieht so ähnlich aus:

    #include <iostream>
    
    TestClassB::TestClassB(int alter, int gewicht, int groesse_in) 
    	: TestClassA(alter,gewicht)
    {
    	groesse = groesse_in;
    }
    
    std::ostream & operator << ( std::ostream & stream , PKW const & data ) 
    { 
    	return stream <<data.alter<<" "<<data.gewicht<<" "<<data.groesse;
    }
    

    Wenn ich Objekte dieser Klassen jetzt in einen Vektor stecke:

    Testi.push_back(TestClassA(25,70));
    Testi.push_back(TestClassB(40,80,180));
    Testi.push_back(TestClassB(12,40,140));
    

    und danach versuche mit den überschriebenen STDOUT's die Daten wieder rauszulesen wird immer nur die überschriebene Varainte der Superklasse verwendet - ich bekomme also konkret nie die Größe heraus :(:(

    cout << Testi[0];   --> 25 70
    cout << Testi[1];   --> 40 80
    cout << Testi[2];   --> 12 40
    

    Ich hätte aber gerne dass jede Klasse den eigenen, überschriebenen STDOUT verwendet

    😕 😕



  • TanjaAnke schrieb:

    Hallo Leute !!

    Ich versuch mich grad ein bisschen an der Vererbung und hänge bei folgendem Problem:

    Ich habe 1 Klasse TestClassA und 2 Klassen die davon erben TestClassB und TestClassC.

    Für die verbesserte Ausgabe habe ich jeweils STDOUT überschrieben

    In TestClassA hab ich 2 Variablen und in den Unterklassen jeweils eine weitere Variable damit die Vererbung auch halbwegs sinnvoll ist:

    #include <iostream>
    
    TestClassA::TestClassA(int alter_in, int gewicht_in)
    {
    alter=alter_in;
    gewicht=gewicht_in;	
    }
    
    std::ostream & operator << ( std::ostream & stream , Fahrzeug const & data  ) 
    { 
      return stream <<data.alter<<" "<<data.gewicht;
    }
    

    Die vererbte Klasse sieht so ähnlich aus:

    #include <iostream>
    
    TestClassB::TestClassB(int alter, int gewicht, int groesse_in) 
    	: TestClassA(alter,gewicht)
    {
    	groesse = groesse_in;
    }
    
    std::ostream & operator << ( std::ostream & stream , PKW const & data ) 
    { 
    	return stream <<data.alter<<" "<<data.gewicht<<" "<<data.groesse;
    }
    

    Wenn ich Objekte dieser Klassen jetzt in einen Vektor stecke:

    Testi.push_back(TestClassA(25,70));
    Testi.push_back(TestClassB(40,80,180));
    Testi.push_back(TestClassB(12,40,140));
    

    und danach versuche mit den überschriebenen STDOUT's die Daten wieder rauszulesen wird immer nur die überschriebene Varainte der Superklasse verwendet - ich bekomme also konkret nie die Größe heraus :(:(

    cout << Testi[0];   --> 25 70
    cout << Testi[1];   --> 40 80
    cout << Testi[2];   --> 12 40
    

    Ich hätte aber gerne dass jede Klasse den eigenen, überschriebenen STDOUT verwendet

    😕 😕

    Du schreibst dir eine extra Elementfunktion, welche die Ausgabe uebernimmt und
    machst diese virtuell:

    class TestClassA {
        private:
            //...
        public:
            virtual void write(std::ostream& out) const {
                out<<x<<y;
            }
    };
    
    class TestClassB : public TestClassA {
        private:
            //...
        public:
            virtual void write(std::ostream& out) const {
                out<<z<<x;
            }
    };
    

    Und ueberschreibst dir den op<< und rufst dort diese Elementfunktion auf:

    std::ostream& operator<<(std::ostream& out, const TestClassA& testClass) {
        testClass.write(out);
        return out;
    }
    

    mfg
    v R



  • müssen die 3 methoden in TestClassA, TestClassB und TestClassC virtuell sein ?

    heißt das Schlüsselwort "virtual" soviel wie "davon gibts mehrere, such dir die, die am besten zum Objekt passt" ?



  • Dein Vektor ist vermutlich

    vector<TestClassA>
    

    . Damit kann nur noch der Stream Operator << (ostream &, TestClassA) für ein Vektorelement aufgerufen werden. Insbesondere sind in Deinem Vektor auch nie irgendwelche TestClassB drin. Auch wenn Du sie mit push_back in den Vector gesteckt hast. Dabei wird automatisch ein TestClassA aus dem TestClassB erzeugt (Kopie). Damit Du in Deinem Vector beide Klassen haben kannst, solltest Du

    vector<TestClassA*>
    

    verwenden.

    Damit hättest Du dann TestClassB Elemente in Deinem Vector, beim Aufruf

    cout << *Testi[0] << endl;
    

    kann Dein Compiler aber wieder nur den Stream-Operator <<( ostream&, TestClassA) nehmen. Eine andere Typ-Information ist zur Compile-Zeit ja nicht vorhanden.

    Um Dein gewünschtest Ergebnis zu erzielen, gehst Du besser so wie virtuell Realisticer inzwischen vorgeschlagen hat...



  • TanjaAnke schrieb:

    müssen die 3 methoden in TestClassA, TestClassB und TestClassC virtuell sein ?

    heißt das Schlüsselwort "virtual" soviel wie "davon gibts mehrere, such dir die, die am besten zum Objekt passt" ?

    Ist eine Elementfunktion als virtual deklariert und gibt es selbige auch in der
    von einer Klasse Abgeleiteten Klasse, so wird beim Aufruf der Funktion
    automatisch die richtige Funktion aufgerufen. Hierzu wird intern eine sogenannte
    vtable (virtual function table) erstellt, welche die Zeiger auf die "richtigen"
    virtuellen Funktionen enthaelt.

    mfg
    v R



  • Das Problem bei den virtuellen Klassen ist, dass ich dann keinen Zugriff auf die privaten Variablen der Superklasse habe.

    class TestClassB : public TestClassA {
        private:
            //...
        public:
            virtual void write(std::ostream& out) const {
                out<<z<<x;
            }
    };
    

    Das 'x' aus der TestClassA ist hier unten leider nicht zugreifbar, egal ob ichs public oder private deklariere.



  • und zu

    virtual void write(std::ostream& out) const { 
                out<< alter << " " << gewicht;
    }
    

    sagt mein Compiler nur

    :\test\TestClassA.cpp(36) : error C2270: "write" : Modifizierer für Funktionen, die keine Member-Funktionen sind, nicht zulässig
    

    😞 😮 😞



  • TanjaAnke schrieb:

    Das Problem bei den virtuellen Klassen ist, dass ich dann keinen Zugriff auf die privaten Variablen der Superklasse habe.

    class TestClassB : public TestClassA {
        private:
            //...
        public:
            virtual void write(std::ostream& out) const {
                out<<z<<x;
            }
    };
    

    Das 'x' aus der TestClassA ist hier unten leider nicht zugreifbar, egal ob ichs public oder private deklariere.

    Wenn du sie als public deklarierst, dann musst du drauf zugreifen koennen.
    Alternativ kannst du diese noch in den protected-Bereich deklarieren oder du
    stellst Elementfunktionen bereit, mit deren Hilfe du auf die Klassenelemente
    zugreifen kannst.

    mfg
    v R



  • TanjaAnke schrieb:

    und zu

    virtual void write(std::ostream& out) const { 
                out<< alter << " " << gewicht;
    }
    

    sagt mein Compiler nur

    :\test\TestClassA.cpp(36) : error C2270: "write" : Modifizierer für Funktionen, die keine Member-Funktionen sind, nicht zulässig
    

    😞 😮 😞

    Das 'virtual' taucht nur in der Deklaration, innerhalb der Klasse auf. Nicht
    bei der Definition in der .cpp-Datei. Ausserdem musst du hier dann auch
    voll qualifizieren, d. h. du musst 'void TestClassA::write(std::ostream& out) const'
    schreiben.

    mfg
    v R



  • ich hab das jetzt mal auf

    36   --   virtual void TestClassA::write(std::ostream& out) const { 
    37   --               out<< alter << " " << gewicht << " " ;
    38   --   }
    

    geändert - dann komm ich wieder zu den variablen

    im H-File hab ich unter public

    virtual void write (std::ostream& out)const;
    

    drinnen

    und die Fehlermeldung lautet

    test\TestClassA.cpp(36) : error C2723: 'write' : Speicherklassenbezeichner 'virtual' ungueltig fuer Funktionsdefinition
    Fehler beim Ausführen von cl.exe.
    

    Warum geht das nicht ? 😞 😞



  • da haben wir uns jetzt überkreuzt 🙂

    deine letzte Antwort hat mir mein Problem gelöst !

    Danke 😃 👍



  • Es ist leider noch immer so, dass die WRITE-Methode der Superklasse aufgerufen wird - ganz egal welches Objekt ich in den Vector stecke :(:(

    woran kanns da noch liegen ?

    vielleicht erbarmt sich noch jemand meiner... 🙂



  • TanjaAnke schrieb:

    Es ist leider noch immer so, dass die WRITE-Methode der Superklasse aufgerufen wird - ganz egal welches Objekt ich in den Vector stecke :(:(

    woran kanns da noch liegen ?

    vielleicht erbarmt sich noch jemand meiner... 🙂

    Zeig mal den Code, der die Instanzen in den vector packt.

    mfg
    v R



  • wie "niemand" vorgeschlagen hat, hab ich jetzt den Vektor in

    std::vector<TestClassA*> Testi;
    

    geändert

    Beim Aufruf von

    Testi.push_back(TestClassB(alter,gewicht,groesse));
    

    kommt dann plötzlich

    :\test\test.cpp(97) : error C2664: 'push_back' : Konvertierung des Parameters 1 von 'class TestClassB' in 'class TestClassA *const & ' nicht moeglich
    Ursache: Konvertierung von 'class TestClassB' in 'class TestClassA *const ' nicht moeglich



  • Testi.push_back(new TestClassB(alter,gewicht,groesse));
    

    delete nicht vergessen
    Kurt



  • Es muss

    Testi.push_back( new TestClassB( alter,gewicht,groesse));
    

    heissen.



  • Das ist auch richtig. Du speicherst jetzt einen Zeiger auf TestClassA-Instanzen
    ab und musst natuerlich auch entsprechende liefern:

    Testi.push_back(new TestClassB(alter,gewicht,groesse));
    

    Du erstellst jetzt dynamisch eine Instanz der Klasse TestClassB, welche an
    'TestClassA*' zugewiesen werden kann.

    Was du auf jeden Fall jetzt beachten musst: Du reservierst Speicher dynamisch und
    musst diesen folglich auch wieder freigeben:

    //irgendwo bei programm ende
    for(size_t i = 0; i < Testi.size(); ++i)
        delete Testi[i];
    

    mfg
    v R



  • Es ist zwar jetzt so dass die Fehlermeldung weg ist - der Aufruf der write-Methode erreicht aber jetzt weder die der SuperKlasse noch die der erbenden Klasse.

    Und laut Debugger ist da wieder nur ein TestClassA-Objekt drinnen - d.h. nur die Basis-Variablen sind vorhanden

    PS: ich habe jetzt auch mit dem Debugger festgestellt dass beim Aufruf, also bei

    Testi.push_back(new TestClassB(alter,gewicht,groesse));
    

    alle Werte richtig befüllt wurden...

    Das Ganze wird dann an den Konstruktor

    TestClassB::TestClassB(int alter, int gewicht, int groesse_in) 
    	: TestClassA(alter, gewicht)
    {
    	groesse = groesse_in;
    }
    

    und trotzdem kommt am schluss immer ein TestClassA-Objekt in den Vector....



  • TanjaAnke schrieb:

    Es ist zwar jetzt so dass die Fehlermeldung weg ist - der Aufruf der write-Methode erreicht aber jetzt weder die der SuperKlasse noch die der erbenden Klasse.

    Und laut Debugger ist da wieder nur ein TestClassA-Objekt drinnen - d.h. nur die Basis-Variablen sind vorhanden

    Selbstverstaendlich ist nur ein TestClassA-Objekt im vector enthalten. Aber da
    TestClassB und TestClassC von TestClassA erben, ist diese Zuweisung legitim.

    Zu deinem Problem:
    Kannst du den Code mal hier: http://rafb.net/paste/ posten und den Link hier
    reinstellen?

    mfg
    v R



  • Ich habe jetzt gleichmal das Original gepostet und nicht mehr meine Fantasie-Klassen 🙂

    Es gibt die Klasse Fahrzeug = Oberklasse = entspricht TestClassA
    und Klasse PKW = erbende Klasse = entspricht TestClassB

    Die Main ist in einem weiteren externen File und arbeitet mit dem Vector

    http://rafb.net/paste/results/iLaPZm53.html


Anmelden zum Antworten