Polymorphie Problem



  • Hallo!

    Ich bin gerade am C++ lernen. Ich kann Java bereits relativ gut.
    Ein Programm, das ich in Java geschrieben habe, möchte ich in C++ auch gerne schreiben.
    Ich habe im Prinzip alles analog zu meinem Java-Code gemacht, jedoch stoße ich auf ein Problem.
    Die Oberklasse ist Mitarbeiter. Die beiden Unterklassen sind Angestellter und Arbeiter.
    Ich habe einen vector<Mitarbeiter&>.
    Zu diesem kann ich ein Angestellter Objekt hinzufügen, jedoch kein Arbeiter Objekt. Beim push_back mit dem Arbeiter erhalte ich folgende Fehlermeldung:

    1 IntelliSense: Ein Verweis vom Typ ""Mitarbeiter &"" (nicht konstantenqualifiziert) kann nicht mit einem Wert vom Typ ""Arbeiter"" initialisiert werden. d:\visual studio 2013\Projects\Personalabrechnung\Personalabrechnung\main.cpp 62 28 Personalabrechnung

    Warum kann ich den Arbeiter nicht hinzufügen?

    Hier der Code:
    main.cpp

    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <cstdlib>
    #include <cstdio>
    #include <clocale>
    #include <vector>
    #include <map>
    #include <algorithm>
    #include "Mitarbeiter.h"
    #include "Arbeiter.h"
    #include "Angestellter.h"
    using namespace std;
    
    int main() {
    	setlocale(LC_ALL, "German");
    
    	const string inPath = "personalData.csv";
    	map<unsigned int, vector<Mitarbeiter&>> mymap;
    	ifstream in(inPath);
    
    	if (!in) {
    		cerr << "Fehler beim Einlesen" << endl;
    		return EXIT_FAILURE;
    	}
    
    	string buffer;
    	while (getline(in, buffer)) {
    		istringstream str(buffer);
    		vector<string> parts;
    		string token;
    		while (getline(str, token, ';')) {
    			parts.push_back(token);
    		}
    		unsigned int blz;
    		istringstream(parts.at(6)) >> blz;
    		if (mymap.find(blz) == mymap.end()) {
    			mymap.insert(make_pair(blz, vector<Mitarbeiter&>()));
    		}
    		if (parts.at(3).empty()) {
    			unsigned int persNr;
    			istringstream(parts.at(0)) >> persNr;
    			unsigned int ktoNr;
    			istringstream(parts.at(7)) >> ktoNr;
    			double pauschalGehalt;
    			replace(parts.at(5).begin(), parts.at(5).end(), ',', '.');
    			istringstream(parts.at(5)) >> pauschalGehalt;
    			Angestellter a(persNr, parts.at(1), parts.at(2), blz, ktoNr, pauschalGehalt);
    			mymap.at(blz).push_back(a);
    		} else {
    			unsigned int persNr;
    			istringstream(parts.at(0)) >> persNr;
    			unsigned int ktoNr;
    			istringstream(parts.at(7)) >> ktoNr;
    			double stdLohn;
    			replace(parts.at(3).begin(), parts.at(5).end(), ',', '.');
    			istringstream(parts.at(3)) >> stdLohn;
    			double anzlStd;
    			replace(parts.at(4).begin(), parts.at(5).end(), ',', '.');
    			istringstream(parts.at(4)) >> anzlStd;
    			Arbeiter a(persNr, parts.at(1), parts.at(2), blz, ktoNr, stdLohn, anzlStd);
    			mymap.at(blz).push_back(a); // Hier kommt die Fehlermeldung
    		}
    	}
        in.close();
        // Hier würde ich dann die ganzen Mitarbeiter nach BLZ getrennt in Dateien schreiben (1 Datei pro BLZ)
    
    	return EXIT_SUCCESS;
    }
    

    Mitarbeiter.h

    #include <string>
    #include <iostream>
    
    class Mitarbeiter {
    	unsigned int persNr;
    	std::string vorname;
    	std::string nachname;
    	unsigned int blz;
    	unsigned int ktoNr;
    protected:
    	virtual std::string toString() const;
    public:
    	Mitarbeiter(unsigned int persNr, std::string vorname, std::string nachname, unsigned int blz, unsigned int ktoNr);
    	unsigned int getPersNr() const;
    	std::string getVorname() const;
    	std::string getNachname() const;
    	unsigned int getBLZ() const;
    	unsigned int getKtoNr() const;
    	virtual double getGehalt() const = 0;
    	friend std::ostream& operator<<(std::ostream& ostr, Mitarbeiter& m);
    };
    

    Mitarbeiter.cpp

    #include "Mitarbeiter.h"
    #include <string>
    #include <iostream>
    #include <sstream>
    
    std::string Mitarbeiter::toString() const {
    	std::ostringstream str;
    	str << getKtoNr() << ";" << getVorname() << ";" + getNachname() << ";" << getGehalt();
    	return str.str();
    }
    
    Mitarbeiter::Mitarbeiter(unsigned int persNr, std::string vorname, std::string nachname,
    						 unsigned int blz, unsigned int ktoNr) : persNr(persNr), vorname(vorname), nachname(nachname), blz(blz), ktoNr(ktoNr) {}
    
    unsigned int Mitarbeiter::getPersNr() const {
    	return persNr;
    }
    
    std::string Mitarbeiter::getVorname() const {
    	return vorname;
    }
    
    std::string Mitarbeiter::getNachname() const {
    	return nachname;
    }
    
    unsigned int Mitarbeiter::getBLZ() const {
    	return blz;
    }
    
    unsigned int Mitarbeiter::getKtoNr() const {
    	return ktoNr;
    }
    
    std::ostream& operator<<(std::ostream& ostr, Mitarbeiter& m) {
    	return ostr << m.toString();
    }
    

    Arbeiter.h

    #include "Mitarbeiter.h"
    
    class Arbeiter : public Mitarbeiter {
    	double stdLohn;
    	double anzlStd;
    public:
    	Arbeiter(unsigned int persNr, std::string vorname, std::string nachname, unsigned int blz, unsigned int ktoNr, double stdLohn, double anzlStd);
    	double getStdLohn() const;
    	double getAnzlStd() const;
    	virtual double getGehalt() const;
    	friend std::ostream& operator<<(std::ostream& ostr, Arbeiter& m);
    };
    

    Arbeiter.cpp

    #include "Arbeiter.h"
    #include <iostream>
    #include <string>
    #include <sstream>
    
    Arbeiter::Arbeiter(unsigned int persNr, std::string vorname, std::string nachname,
    				   unsigned int blz, unsigned int ktoNr, double stdLohn, double anzlStd) : Mitarbeiter(persNr, vorname, nachname, blz, ktoNr),
    																						   stdLohn(stdLohn), anzlStd(anzlStd) {}
    
    double Arbeiter::getStdLohn() const {
    	return stdLohn;
    }
    
    double Arbeiter::getAnzlStd() const {
    	return anzlStd;
    }
    
    double Arbeiter::getGehalt() const {
    	return stdLohn * anzlStd;
    }
    
    std::ostream& operator<<(std::ostream& ostr, Arbeiter& a) {
    	return ostr << a.toString();
    }
    

    Angestellter.h

    #include "Mitarbeiter.h"
    
    class Angestellter : public Mitarbeiter {
    	double pauschalGehalt;
    public:
    	Angestellter(unsigned int persNr, std::string vorname, std::string nachname, unsigned int blz, unsigned int ktoNr, double pauschalGehalt);
    	double getPauschalGehalt() const;
    	virtual double getGehalt() const;
    	friend std::ostream& operator<<(std::ostream& ostr, Angestellter& a);
    };
    

    Angestellter.cpp

    #include "Angestellter.h"
    #include <string>
    #include <iostream>
    #include <sstream>
    
    Angestellter::Angestellter(unsigned int persNr, std::string vorname, std::string nachname,
    			 unsigned int blz, unsigned int ktoNr, double pauschalGehalt) : Mitarbeiter(persNr, vorname, nachname, blz, ktoNr),
    																			pauschalGehalt(pauschalGehalt) {}
    
    double Angestellter::getPauschalGehalt() const {
    	return pauschalGehalt;
    }
    
    double Angestellter::getGehalt() const {
    	return getPauschalGehalt();
    }
    
    std::ostream& operator<<(std::ostream& ostr, Angestellter& a) {
    	return ostr << a.toString();
    }
    


  • Hallo,

    ich bezweifel, dass du einen Angestellten hinzufügen kannst.
    Keine Ahnung, warum der Kompiler in Zeile 62 anfängt zu meckern, aber wenn du die auskommentierst, wirst du trotzdem nicht kompileren können.

    Ich weiß ehrlich gesagt auch gar nicht, was du dir unter vector<Mitarbeiter**&**> vorstellst.

    Wie eben schon ein paar Threads unter diesem beschrieben, funktioniert runtime polymorphism in C++ stets über Zeiger:

    vector<Mitarbeiter> v1;
    // Hier kannst du keinen Angestellten (sinnvoll) einfügen, schon allein, da Angestellter grösser als Mitarbeiter ist.
    vector<Mitarbeiter*> v2;
    // Prima, Zeiger auf Mitarbeiter kann auch auf einen Angestellten zeigen
    // Speichermanagement musst du aber selber übernehmen
    vector<unique_ptr<Mitarbeiter>> v3;
    // IdR die erste Wahl, siehe Thread unten und mach einfach das template raus
    


  • Jockelx schrieb:

    Hallo,

    ich bezweifel, dass du einen Angestellten hinzufügen kannst.
    Keine Ahnung, warum der Kompiler in Zeile 62 anfängt zu meckern, aber wenn du die auskommentierst, wirst du trotzdem nicht kompileren können.

    Ich weiß ehrlich gesagt auch gar nicht, was du dir unter vector<Mitarbeiter**&**> vorstellst.

    Wie eben schon ein paar Threads unter diesem beschrieben, funktioniert runtime polymorphism in C++ stets über Zeiger:

    vector<Mitarbeiter> v1;
    // Hier kannst du keinen Angestellten (sinnvoll) einfügen, schon allein, da Angestellter grösser als Mitarbeiter ist.
    vector<Mitarbeiter*> v2;
    // Prima, Zeiger auf Mitarbeiter kann auch auf einen Angestellten zeigen
    // Speichermanagement musst du aber selber übernehmen
    vector<unique_ptr<Mitarbeiter>> v3;
    // IdR die erste Wahl, siehe Thread unten und mach einfach das template raus
    

    Hi!
    Erstmal danke für deine Antwort!
    Ok ich habe jetzt vector<unique_ptr<Mitarbeiter>>.
    Ich füge die Subklassen-Objekte jetzt so hinzu:

    mymap.at(blz).push_back(unique_ptr<Mitarbeiter>(&a));
    

    Beim hinzufügen des Arbeiters (beim Angestellten wieder nicht) erhalte ich jetzt folgende Fehlermeldung:

    1 IntelliSense: Keine Instanz des Konstruktors ""std::unique_ptr<_Ty, _Dx>::unique_ptr [mit _Ty=Mitarbeiter, _Dx=std::default_delete<Mitarbeiter>]"" stimmt mit der Argumentliste überein.
    Argumenttypen sind: (Arbeiter 😉 d:\visual studio 2013\Projects\Personalabrechnung\Personalabrechnung\main.cpp 63 28 Personalabrechnung



  • Was Intellisense sagt ist irrelevant! Lass den Compiler laufen.

    Das
    mymap.at(blz).push_back(unique_ptr<Mitarbeiter>(&a));
    wird vielleicht übersetzt, führt aber bestimmt zum Absturz. Ohne new oder make_unique geht das nicht.



  • Jockelx schrieb:

    Wie eben schon ein paar Threads unter diesem beschrieben, funktioniert runtime polymorphism in C++ stets über Zeiger:

    Es geht auch mit Referenzen. Stackobjekte sollte man aber nicht in einen Vector packen.



  • manni66 schrieb:

    Was Intellisense sagt ist irrelevant! Lass den Compiler laufen.

    Das
    mymap.at(blz).push_back(unique_ptr<Mitarbeiter>(&a));
    wird vielleicht übersetzt, führt aber bestimmt zum Absturz. Ohne new oder make_unique geht das nicht.

    Ok ich habe jetzt folgendes gemacht:

    auto ptr = make_unique<Mitarbeiter>(new Angestellter(persNr, parts.at(1), parts.at(2), blz, ktoNr, pauschalGehalt));
    mymap.at(blz).push_back(ptr);
    
    auto ptr = make_unique<Mitarbeiter>(new Arbeiter(persNr, parts.at(1), parts.at(2), blz, ktoNr, stdLohn, anzlStd));
    mymap.at(blz).push_back(ptr);
    

    Jetzt kann ich nicht compilen wegen folgenden Fehlern in der Headerfile memory:

    Fehler 1 error C2259: 'Mitarbeiter': Instanz von abstrakter Klasse kann nicht erstellt werden d:\programs\microsoft visual studio 12.0\vc\include\memory 1639 1 Personalabrechnung

    Fehler 2 error C2664: 'Mitarbeiter::Mitarbeiter(const Mitarbeiter &)' : Konvertierung von Argument 1 von 'Angestellter *' in 'const Mitarbeiter &' nicht möglich d:\programs\microsoft visual studio 12.0\vc\include\memory 1639 1 Personalabrechnung

    Fehler 3 error C2664: 'Mitarbeiter::Mitarbeiter(const Mitarbeiter &)' : Konvertierung von Argument 1 von 'Arbeiter *' in 'const Mitarbeiter &' nicht möglich d:\programs\microsoft visual studio 12.0\vc\include\memory 1639 1 Personalabrechnung

    Aus den Fehlermeldungen von Visual Studio werd ich echt nicht schlau.



  • Die Fehlermeldung passt zwar nicht, aber mach erstmal den ebenfalls im anderen Thread beschriebenen Fehler bzgl. unique_ptr-copy weg:

    mymap.at(blz).push_back(std::move(ptr));
    

    Edit:
    make_unique<Angestellter>...nicht Mitarbeiter!

    genauer:

    auto ptr = make_unique<Angestellter>{persNr, parts.at(1), parts.at(2), blz, ktoNr, stdLohn, anzlStd};
    


  • Jockelx schrieb:

    Die Fehlermeldung passt zwar nicht, aber mach erstmal den ebenfalls im anderen Thread beschriebenen Fehler bzgl. unique_ptr-copy weg:

    mymap.at(blz).push_back(std::move(ptr));
    

    Edit:
    make_unique<Angestellter>...nicht Mitarbeiter!

    Hab ich alles gemacht.
    Ich habe jedoch immer noch 2 Fehler in memory:

    Fehler 1 error C2664: 'Angestellter::Angestellter(const Angestellter &)' : Konvertierung von Argument 1 von 'Angestellter *' in 'const Angestellter &' nicht möglich d:\programs\microsoft visual studio 12.0\vc\include\memory 1639 1 Personalabrechnung

    Fehler 2 error C2664: 'Arbeiter::Arbeiter(const Arbeiter &)' : Konvertierung von Argument 1 von 'Arbeiter *' in 'const Arbeiter &' nicht möglich d:\programs\microsoft visual studio 12.0\vc\include\memory 1639 1 Personalabrechnung



  • Entweder make_unique oder new.



  • manni66 schrieb:

    Entweder make_unique oder new.

    Ok, vielen Dank!
    Jetzt funktioniert es!


Anmelden zum Antworten