Anwendung von Polymorphie und Vererbung - Aufruf einer Methode einer abgeleiteten Klasse



  • Hallo zusammen,

    ich versuche gerade einem C++ Tutorial zu folgen und scheitere an einem "Experiment" das ein bischen von dem Tutorialfahrplan abweicht. Im Moment geht es um das Thema Vererbung und Polymorphie.

    Hier die Klassendefinition der "Superklasse":
    "vehicle.h"

    #pragma once
    #include <string>
    
    
    class Vehicle
    {
    private:
    	int privateVariable = 27;
    
    public:
    	Vehicle();
    	virtual ~Vehicle();
    	virtual std::string getType();
    };
    

    "vehilce.cpp"

    #include <iostream>
    #include "vehicle.h"
    
    Vehicle::Vehicle()
    {
    	std::cout << "Constructor Vehilce" << std::endl;
    }
    
    Vehicle::~Vehicle()
    {
    	std::cout << "Destructor Vehilce" << std::endl;
    }
    
    std::string Vehicle::getType()
    {
    	return "base type";
    }
    

    Hier die abgeleitete Klasse:
    "car.h"

    #pragma once
    #include <iostream>
    #include <string>
    
    #include "vehicle.h"
    
    class Car : public Vehicle
    {
    private:
    	float position = 0.0f;
    	float velocity = 0.0f;
    public:
    	Car();
    	~Car();
    
    	std::string getType();
    	std::string nameBrand();
    };
    
    

    "car.cpp"

    #include "car.h"
    
    Car::Car()
    {
    	std::cout << "Constructor Car" << std::endl;
    }
    
    Car::~Car()
    {
    	std::cout << "Destructor Car" << std::endl;
    }
    
    std::string Car::getType()
    {
    	return "Car";
    }
    
    std::string Car::nameBrand()
    {
    	return "Audi";
    }
    
    

    Hier jetzt die Erzeugung der Objekte:
    "main.cpp"

    #include <iostream>
    #include <string>
    
    #include "vehicle.h"
    #include "car.h"
    
    int main()
    {
    	//Polymorphismus
    	//Erstellen eines Array der Superklasse
    
    	Vehicle* myVehicles[1];
    	myVehicles[0] = new Car();
    
    	std::cout << myVehicles[0]->getType().c_str() << std::endl;
            //std::cout << myVehicles[0]->nameBrand().c_str() << std::endl;
    	delete myVehicles[0];
    	myVehicles[0] = nullptr;
    
    	system("pause");
    	return 0;
    }
    

    Ich kann die Methode "getType()" die auch in der "Vehicle"-Klasse definiert wie gewollt überschreiben.
    Kann man aber auch irgendwie auf die Methode "nameBrand()" zugreifen die nur in der "Car"-Klasse definiert ist? Ich möchte dazu aber kein neues Objekt erstellen sondern das "myVehicles[0]"-Objekt, wenn es geht, beibehalten.

    Ich entschuldige mich jetzt schon für falsche Bezeichungen. Ich bin Anfänger und würde diesen Teil der objektorientierten Programmierung gerne richtig verstehen. Ich weiß auch nicht, ob ich die richtige Kategorie gewählt habe.

    Vielen Dank schonmal im Vorraus und viele Grüße.



  • Nein, das geht nicht.
    myVehicles[0] ist ja vom Typ Vehicle und kann nur auf die dort bekannten Methoden zugreifen.

    Edit:

    Stell Dir vor, Du leitest noch weitere Klassen von Vehicle ab.
    Stell Dir weiter vor, nameBrand() gibt es nur in Car, dafür gibt es in Helicopter getNrOfRotorBlades().
    Nur stell Dir vor, Dein Array von Vehicle hat mehrere Einträge, in myVehicles[0] ist ein Car, in myVehicles[1] ist ein Helicopter usw.

    Wie willst Du später wissen, welche Methoden in myVehicles[i] vorhanden sind?

    Eben, es sind nur die vorhanden, die auch in Vehicle bereits bekannt sind. Aber die können sich dann in den verschiedenen abgeleiteten Klassen unterschiedlich verhalten, indem sie überschrieben werden.



  • Hallo,

    @Belli sagte in Anwendung von Polymorphie und Vererbung - Aufruf einer Methode einer abgeleiteten Klasse:

    Nein, das geht nicht.

    Danke für die schnelle und eindeutige Antwort. Vielen Dank auch für die genauere Erklärung. Ich kann dann jetzt erstmal in der Serie weitermachen und komme bestimmt bald mit weiteren Fragen.

    P.S. kann man einen thread hier irgendwo auf "gelöst" setzen?

    Grüße und danke



  • Noch kurz ein paar weitere Infos:

    1. Du kannst std::strings direkt ausgeben, also std::cout << mystring; - es ist kein .c_str() notwendig. Die Idee der outstreams ist es ja gerade, dass du Objekte einfach mit operator<< ausgibt und sie gerade nicht vorher in etwas anderes umwandelst.

    2. Generell: Du benutzt new und delete. War jetzt im Beispiel ok, aber generell solltest du new und delete möglichst gar nicht bzw. ausschließlich in Ressourcenverwaltungscode nutzen. Aber dieses Fass will ich eigentlich gar nicht hier aufmachen, weil es mit deinem Problem nichts zu tun hat.

    3. Natürlich kannst du auch nameBrand() aufrufen. Wegen der von @Belli genannten Problematik geht das nicht einfach so, sondern du musst vorher casten.
      Also sowas wie

    Car *car_aus_array = dynamic_cast<Car *>(myVehicles[0]); 
    if (car_aus_array) {
        std::cout << car_aus_array->nameBrand() << '\n';
    } else {
        std::cout << "Das Element ist kein Car\n";
    }
    

    (sieh dir dynamic_cast & static_cast an)



  • Hallo,

    @wob
    vielen Dank für deinen Beitrag.
    Das man das .c_str()nicht braucht habe ich auch schon bemerkt. Ich hatte mich bei diesem Code einfach an die Vorgabe aus dem Tutorial gehalten.

    Was genau ist den falsch an dem Einsatz von newund delete? Die Befehle klingen doch schön logisch und einfach.

    Dein Vorschlag mit dem dynamic_cast funktioniert einwandfrei.
    Ich werde mich mal genauer mit diesen Funktionalitäten auseinandersetzen.

    Vielen Dank und viele Grüße



  • @enlighten sagte in Anwendung von Polymorphie und Vererbung - Aufruf einer Methode einer abgeleiteten Klasse:

    Vorgabe aus dem Tutorial

    Das Tutorial ist von übelster Qualität.



  • @enlighten sagte in Anwendung von Polymorphie und Vererbung - Aufruf einer Methode einer abgeleiteten Klasse:

    vielen Dank für deinen Beitrag.
    Das man das .c_str()nicht braucht habe ich auch schon bemerkt. Ich hatte mich bei diesem Code einfach an die Vorgabe aus dem Tutorial gehalten.

    Dann taugt das Tutorial möglicherweise wenig (oder der Autor liebt diesen Fehler). Mit clang-tidy gibt es sogar ein Tool, das automatisch unnötiges .c_str() erkennen kann (https://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-string-cstr.html). Ich habe das überflüssige .c_str() schon öfter gesehen, am häufigsten bei std::ifstream(filename.c_str()).

    Was genau ist den falsch an dem Einsatz von newund delete? Die Befehle klingen doch schön logisch und einfach.

    Das hatten wir erst neulich hier. Du verlierst aus den Augen, wann und wo du new und das zugehörige delete hast. Folge sind Speicherlecks oder doppelte deletes. Wenn eine (ungefangene) Exception zwischen dem new und delete kommt, wird das delete nie aufgerufen. Wenn du in einer Funktion bist und hinter dem new ein early return machen willst, müsstest du in jedem return-Branch delete aufrufen.

    Daher generell: new und delete ausschließlich in Klassen, die ausschließlich für Ressourcenverwaltung zuständig sind, nutzen. Es gibt in C++ dafür auch fertige Smartpointer-Klassen: unique_ptr (sollte der default sein) und shared_ptr (wenn du den Pointerbesitz teilen musst). Versuche all deine new durch std::make_unique zu ersetzen (und die zugehörigen deletes alle zu entfernen).

    Diese Regel ist bekannt unter "no naked new". Google mal danach.

    (die core guidelines sind manchmal etwas unaufgeäumt, finde ich...)

    Dein Vorschlag mit dem dynamic_cast funktioniert einwandfrei.

    Ja, aber ob er auch so empfehlenswert ist, das ist eine andere Frage. Generell ist es eher ein schlechtes Zeichen, wenn du einen Cast verwenden musst. Du sagst hier, du hast eine Menge von Fahrzeugen, aber willst eine spezielle Eigenschaft eines Autos ausnutzen. Dann solltest du vielleicht besser eine Liste von Autos haben. Oder das Fahrzeug sollte eine (ggf. abstrakte) Funktion haben, die die entsprechende Funktionalität von den Kindklassen einfordert. Schwer ohne konkretes Beispiel zu sagen. Noch dazu können dynamic_casts auch Performance kosten.

    https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rh-dynamic_cast

    Zitat unter anderem: "Like other casts, dynamic_cast is overused. Prefer virtual functions to casting. Prefer static polymorphism to hierarchy navigation where it is possible [...] and reasonably convenient."

    Ich werde mich mal genauer mit diesen Funktionalitäten auseinandersetzen.

    Gute Idee 🙂



  • @wob sagte in Anwendung von Polymorphie und Vererbung - Aufruf einer Methode einer abgeleiteten Klasse:

    Ich habe das überflüssige .c_str() schon öfter gesehen, am häufigsten bei std::ifstream(filename.c_str()).

    Der c-tor nahm lange keine std::strings.



  • @Swordfish sagte in Anwendung von Polymorphie und Vererbung - Aufruf einer Methode einer abgeleiteten Klasse:

    Der c-tor nahm lange keine std::strings.

    Seit ich C++ nutze, geht das. Ich habe mit -std=c++0x angefangen. Davor ging es nicht.



  • @wob sagte in Anwendung von Polymorphie und Vererbung - Aufruf einer Methode einer abgeleiteten Klasse:

    Seit ich C++ nutze, geht das. Ich habe mit -std=c++0x angefangen. Davor ging es nicht.

    Die ISO Norm von 1998 und 2003 konnten es jedenfalls nicht, und wie man immer wieder sehen kann, sind viele der Tutorials immer noch nicht auf den Stand von C++11 oder später aktualisiert worden.


Log in to reply