Vererbung und Überschreiben von STDOUT



  • 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



  • Hallo,

    die 'friend'-Deklarationen benoetigst du nicht. Das Problem das du hast:
    Du hast lediglich eine Definition von
    'std::ostream& operator<<(std::ostream& out, const Fahrzeug& val)' in der
    .cpp-Datei. Du musst diese aber auch in der Header haben, sonst ist der op<<
    ja gar nicht bekannt.

    Mir ist noch aufgefallen, dass es hier Fahrzeug* sein muss. Dein Programm sieht
    also so aus:

    fahrzeug.h

    #ifndef FUHRPARK_FAHRZEUG_H_INCLUDED
    #define FUHRPARK_FAHRZEUG_H_INCLUDED
    
    #include <string>
    
    class Fahrzeug
    {
    	public:
    		Fahrzeug(char car_type[20],char plate[8],int age, int performance);
    		~Fahrzeug();
    
    		void change_plate(char plate[8]);
    		void show_car();
    		virtual void write (std::ostream& out)const;
    
    		char car_type[20];
    		char plate[8];
    		int age;
    		int performance;
    };
    
    std::ostream& operator<<(std::ostream& out, const Fahrzeug* val);
    
    #endif
    

    fahrzeug.cpp

    #include <iostream>
    #include <string>
    #include "Fahrzeug.hpp"
    
    using namespace std;
    
    Fahrzeug::Fahrzeug(char car_type_in[20], char plate_in[8],int age_in, int performance_in)
    {
    	cout << "Neues Fahrzeug eingestellt " << endl;
    	strcpy(car_type,car_type_in);
    	strcpy(plate,plate_in);
    	age = age_in;
    	performance = performance_in;
    }
    
    Fahrzeug::~Fahrzeug()
    {
    }
    
    void Fahrzeug::change_plate(char input[])
    {
    	strcpy(plate,input);
    }
    
    void Fahrzeug::show_car()
    {
    	cout << "Kennzeichen: ";
    	for (int i=0;i<8;i++) {
    		cout << plate[i];
    	}
    	cout << "      Baujahr: "<< age <<"      Leistung: "<< performance << endl;
    }
    
    void Fahrzeug::write(std::ostream& out) const { 
    	cout << "TEST12345";
    }
    
    std::ostream& operator<<(std::ostream& out, const Fahrzeug* val) { 
        val->write(out); 
        return out; 
    }
    

    pkw.h

    #ifndef pkwHPP
    #define pkwHPP
    #include <string>
    #include "fahrzeug.hpp"
    
    class PKW : public Fahrzeug
    {
    	public:
    		PKW(char car_type[20],char plate[8],int age, int performance,int seat,bool child_carrier);
    		virtual ~PKW();
    
    		void set_child_carrier(bool x);
    		virtual void write (std::ostream& out)const;
    
    	private:
    		int seat;
    		bool child_carrier;
    };
    
    #endif
    

    pkw.cpp

    #include <iostream>
    #include <string>
    #include "pkw.hpp"
    
    using namespace std;
    
    PKW::PKW(char car_type[], char plate[],int age, int performance, int seat_in, bool child_carrier_in) 
    	: Fahrzeug(car_type,plate,age,performance)
    {
    	seat = seat_in;
    	child_carrier = child_carrier_in;
    }
    
    PKW::~PKW()
    {
    }
    
    void PKW::set_child_carrier(bool x){
    	child_carrier = x;
    }
    
    void PKW::write(std::ostream& out) const { 
        out << "Ein PKW der Marke: ";
    	int i=0;
    	while (car_type[i]!='\0') {
    		out << car_type[i];
    		i++;
    	}
    	out << "\nKennzeichen: ";
    	i=0;
    	while (plate[i]!='\0') {
    		out << plate[i];
    		i++;
    	}
    	out<<"\nBaujahr: "<<age<<"\nLeistung: "<<performance<<"\nSitze: "<<seat;
    	if (child_carrier) out<<"\nEs ist ein Kindersitz vorhanden";
    	else out<<"\nEs ist kein Kindersitz vorhanden";
    
    }
    

    main.cpp

    #include <iostream>
    #include <vector> 
    #include <string>
    #include "fahrzeug.hpp"
    #include "pkw.hpp"
    
    using namespace std;
    
    int main() {
        std::vector<Fahrzeug*> Fahrzeuge;
        Fahrzeuge.push_back(new PKW("OPEL","ABC",1980,75,5,true));
        Fahrzeuge.push_back(new PKW("VW","DEF",1990,90,5,false));
        Fahrzeuge.push_back(new PKW("VOLVO","GHI",1996,110,4,true));
    
        cout << "\n\n"<<Fahrzeuge.size()<<"\n\n";
        //< nicht <= !!!
        for (int i=0; i<Fahrzeuge.size();i++) 
            cout << Fahrzeuge[i] <<endl; 
        return 0;
    }
    

    mfg
    cu



  • SUUUPER - GANZ HERZLICHEN DANK !! 😃 😃

    Jetzt funktioniert es wie es soll - und gibt auch aus was es soll 👍 👍

    Ich habe nur noch eine einzige winzigkleine Frage, obwohl ich jetzt schon ein relativ schlechtes Gewissen habe 🙄

    Ich lege ja ein fixes Char-Array der Größe 20 an um die Typenbezeichnung einzulesen.
    Falls jemand mehr als diese 20 Stellen eingibt wird der String eben abgeschnitten und setze an die letzte Stelle ein '\0'.

    Falls aber jemand z.B. nur 5 Zeichen eingibt wird trotzdem an die 20ste Stelle das '\0' gesetzt - und ich bekomme je nach Vorbelegung des Speichers einen Haufen Mist in die Ausgabe.

    Lässt sich so ein Char-Array irgendwie einfach mit '\0' vorbelegen ?
    Oder je nach Länge der Eingabe irgendwie "abschneiden" ?

    char type [20];
    cin >> type;
    type[19]='\0';
    

    Ein glückliches Programmier-Mädel 😃


Anmelden zum Antworten