Vector von Basisklasse und Aufruf virtueller Funktionen
-
Hallo liebes Forum! Ich komme aus der Java Welt und stolpere über ein wahrscheinlich triviales C++ Problem.
Ich habe eine Instanz von vector<Fahrzeug> und möchte über alle Fahrzeuge iterieren. Die Klasse Fahrzeug hat eine virtual print() Methode, die in den jeweiligen Klassen überschrieben ist und den Typ ausgibt, ob es eine Subklasse von Auto, LKW oder Motorrad ist.
Nun mein Problem liegt in folgendem Code:
for(vector<Fahrzeug>::iterator i = v.begin(); i != v.end(); ++i) { (*i).print(); }In Java würde dieser Code jeweils den entsprechenden Typ (also z. B. Auto) ausgeben, in meinem Beispiel aber immer Fahrzeug. Eine google Recherche brachte den Begriff Slicing hervor, allerdings verstehe ich nicht, wie ich dieses Problem lösen kann?!

-
Willkommen im Forum!
Ja, dein ergoogelter Begriff ist richtig. Da C++ keine Indirektionen aufzwingt, wird hier der Teil der abgeleiteten Klasse einfach abgeschnitten. Indem du jetzt selbst Zeiger verwendest, kannst du das Problem lösen:
vector<Fahrzeug*>Bzw noch besser:
vector<unique_ptr<Fahrzeug>>Das Problem bei Zeigern im Vector ist nämlich dass der Vector die Objekte hinter den Zeigern nicht automatisch mitlöscht. Dazu dient unique_ptr. Genaueres schlägst du am besten nach.

Grüße,
Der Kellerautomat
-
Du brauchst einen Basisklassenzeiger. Also
std::vector<Fahrzeug*> v; v.push_back( new Auto("Golf GTI Ich hab keine Ahnung von Autos") );Dann wird die virtuelle Methode, wenn sie in Auto überschrieben wurde, auch in Auto aufgerufen (also die, die in Auto definiert wurde und nicht die in der Basisklasse).
Hol dir ein gutes Buch, Thread gibts im Forum.
Versuch am besten aus Fahrzeug eine abstrakte Basisklasse zu machen.
-
Kellerautomat schrieb:
Das Problem bei Zeigern im Vector ist nämlich dass der Vector die Objekte hinter den Zeigern nicht automatisch mitlöscht. Dazu dient unique_ptr. Genaueres schlägst du am besten nach.

Dann kann er gleich
boost::ptr_vectornehmen
-
jule86 schrieb:
Ich komme aus der Java Welt und stolpere über ein wahrscheinlich triviales C++ Problem.
for(vector<Fahrzeug>::iterator i = v.begin(); i != v.end(); ++i) { (*i).print(); }Da muss man dich loben. Die meisten, die von Java kommen, halten großen Abstand von der STL und handeln sich dadurch nur Probleme ein.
Es wird immer eine Kopie des Objekts in den
vectoreingefügt.Auto a; Fahrzeug f = a;Bei der Kopie
f = awerden natürlich nurFahrzeug-Informationen, dieahat, kopiert (alles andere wäre auch sehr unlogisch). Damit ist dir hoffentlich auch klar, warum immer die print-Methode vonFahrzeugaufgerufen wird.
Lösung: Referenzen oder Zeiger verwenden. Referenzen kann ein Container nicht aufnehmen, damit bleiben dir nur noch die Zeiger. Beim Einfügen in denvectorwird dann der Zeiger kopiert. Das eigentliche Objekt bekommt von alldem nichts mit. Da deine Klassen polymorph sind, wird dann je nach Datentyp des Pointees die entsprechende Methode aufgerufen, genauso, wie du es dir erwartet hast.#include <iostream> #include <iterator> #include <vector> using namespace std; class Fahrzeug { public: virtual void print() = 0; }; class Auto : public Fahrzeug { public: virtual void print() { cout << '\n' << "Auto"; } }; class LKW : public Fahrzeug { public: virtual void print() { cout << '\n' << "LKW"; } }; class Motorrad : public Fahrzeug { public: virtual void print() { cout << '\n' << "Motorrad"; } }; int main() { vector<Fahrzeug*> fahrzeuge; Auto a; LKW l; Motorrad m; fahrzeuge.push_back(&a); fahrzeuge.push_back(&l); fahrzeuge.push_back(&m); for(vector<Fahrzeug*>::iterator it=fahrzeuge.begin(); it!=fahrzeuge.end(); ++it) { (*it)->print(); } return 0; }Siehe auch: http://stackoverflow.com/questions/2394581/pure-virtual-class-and-collections-vector
-
Gugelmoser schrieb:
class Fahrzeug { public: virtual void print() = 0; };Da fehlt ein virtueller Destruktor, falls der Vektor die Objekte besitzen und korrekt zerstören soll.
class Fahrzeug { public: virtual ~Fahrzeug() {} virtual void print() = 0; };
-
Da fehlt ein virtueller Destruktor, falls der Vektor die Objekte besitzen und korrekt zerstören soll.
Und falls nicht, tut man gut daran den Destruktor non-virtual protected zu machen.