Mehrfachvererbung und gleichlautende Methoden in beiden Vaterklassen
-
Hallo,
ich habe folgende Klassen geschrieben:
Fortbewegungsmittel, Fahrzeug, Schiff, Luftkissenboot.
Dabei erbt:
Fahrzeug von Fortbewegungsmittel
Schiff von Fortbewegungsmittel
Luftkissenboot von Schiff und FahrzeugFortbewegungsmittel
#include <iostream> using namespace std; #pragma once class Fortbewegungsmittel { protected: float x,y; public: Fortbewegungsmittel(float x_,float y_):x(x_),y(y_){} string sag_was() { ostringstream os; os <<"Meine Position ist " << "x=" << x << " y=" <<y <<" "; string s; s+=os.str(); return s; } void set_all(float x_, float y_) { x=x_; y=y_; } void set_x(float x_) { x=x_; } void set_y(float y_) { y=y_; } float get_x() { return x; } float get_y() { return y; } };
Schiff
#include <sstream> #include <string> #include "Fortbewegungsmittel.h" #pragma once class Schiff:public Fortbewegungsmittel { public: float gewicht; public: Schiff(float x_, float y_, float gewicht_):Fortbewegungsmittel(x_,y_), gewicht(gewicht_){} string ich_befinde_mich() { string s=" Ich befinde mich im Wasser"; return s; } string sag_was() { ostringstream os; os << Fortbewegungsmittel::sag_was() << "gewicht=" << gewicht <<" Tonnen."; string s; s=os.str(); return s; } void set_gewicht(float gewicht_) { gewicht=gewicht_; } float get_gewicht() { return gewicht; } };
Fahrzeug
#include <sstream> #include <string> #include "Fortbewegungsmittel.h" #pragma once class Fahrzeug: public Fortbewegungsmittel { public: float maximal_Geschwindigkeit; public: Fahrzeug(float x_, float y_, float maximal_Geschwindigkeit_):Fortbewegungsmittel(x_,y_), maximal_Geschwindigkeit(maximal_Geschwindigkeit_){} string ich_befinde_mich() { string s=" Ich befinde mich auf Land"; return s; } string sag_was() { ostringstream os; os<<Fortbewegungsmittel::sag_was() << "max Geschwindigkeit=" << maximal_Geschwindigkeit <<"."; string s; s+=os.str(); return s; } float get_maximal_Geschwindigkeit() { return maximal_Geschwindigkeit; } void set_maximal_Geschwindigkeit(float maximal_Geschwindigkeit_) { maximal_Geschwindigkeit=maximal_Geschwindigkeit_; } };
Luftkissenboot
#include "Fahrzeug.h" #include "Schiff.h" #pragma once class Luftkissenboot:Schiff,Fahrzeug { public: Luftkissenboot(float x_, float y_,float gewicht_, float maximal_Geschwindigkeit_ ): Schiff(x_+1,y_+1,gewicht_),Fahrzeug(x_-1,y_-1,maximal_Geschwindigkeit_) {} string ich_befinde_mich() { string s= Fahrzeug::ich_befinde_mich(); s+=" ODER "; s+= Schiff::ich_befinde_mich(); return s; } string sag_was() { ostringstream os; os<<Fortbewegungsmittel::sag_was() << "max Geschwindigkeit=" << maximal_Geschwindigkeit <<"."; /////////Hier !!!!!!!!!!!!!!!!!!!! string s; s+=os.str(); return s; }
Nun habe ich folgendermaßen ein Luftkissenboot erzeugt:
Luftkissenboot* lu= new Luftkissenboot(10,11,600,180); cout << lu->sag_was()<<lu->ich_befinde_mich() << "\n";
Als Ausgabe erhalte ich :
Meine Position ist x=11 y=12 max geschwindigkeit=180. Ich befinde mich auf Land. ODER Ich befinde mich im Wasser
Ich weise im Konstruktor von Luftkissenboot den Vaterklassen Schiff und Fahrzeug unterschiedliche x und y Were zu:
Schiff(x_+1,y_+1,gewicht_),Fahrzeug(x_-1,y_-1,maximal_Geschwindigkeit_)
Warum gibt die Methode Fortbewegungsmittel::sag_was() x=11,y=12 aus und nicht x=9, y=10.
Woran entscheidet sich welche x und y Werte Fortbewegungsmittel ausgibt, da dies nicht eindeutig ist? (Vom Schiff oder vom Fahrzeug)
-
MisterX schrieb:
Woran entscheidet sich welche x und y Werte Fortbewegungsmittel ausgibt, da dies nicht eindeutig ist? (Vom Schiff oder vom Fahrzeug)
Da dies nicht eindeutig ist, ist das ein Fehler, den der Compiler erkennen muss. Wenn er das nicht tut, handelt es sich um einen Bug oder eine Erweiterung des Compilers.
-
Solange du nicht virtuell erbst, nimmt VC++ die erste Klasse, von der du ableitest und die eine geeignete Funktionssignatur hat.
-
Michael E. schrieb:
Solange du nicht virtuell erbst, nimmt VC++ die erste Klasse, von der du ableitest und die eine geeignete Funktionssignatur hat.
Ich würde sogar sagen, dass es im Wesentlichen Ersteres ist: Er nimmt den ersten NAMEN. Wenn es mehrere davon gibt und es sich um Funktionen handelt, schlägt overloading zu und es wird ein "best match" gesucht ... aber es wird nicht mehr in weiteren Basisklassen gesucht:
struct A { void f(int); }; struct B : A { void f(string); }; struct C : B { void g() { f(3); } // Compiler-Error: Keine Funktion mit passender Signatur gefunden };
Gruß,
Simon2.
-
Simon2 schrieb:
Michael E. schrieb:
Solange du nicht virtuell erbst, nimmt VC++ die erste Klasse, von der du ableitest und die eine geeignete Funktionssignatur hat.
Ich würde sogar sagen, dass es im Wesentlichen Ersteres ist: Er nimmt den ersten NAMEN. Wenn es mehrere davon gibt und es sich um Funktionen handelt, schlägt overloading zu und es wird ein "best match" gesucht ... aber es wird nicht mehr in weiteren Basisklassen gesucht:
Gruß,
Simon2.
Das ist etwas völlig anderes: hier wird die Funktion A::f durch B::f verdeckt. Der Compiler beschwert sich völlig zu recht.
Die Problematik im urprünglichen Fall besteht weder beim Name lookup noch bei der Overloadauflösung. Problematisch ist dagegen die implizite Konvertierung des Objektparameters:
Fortbewegungsmittel::sag_was() // ist äquivalent zu this->Fortbewegungsmittel::sag_was() // also static_cast<Fortbewegungsmittel*>(this)->Fortbewegungsmittel::sag_was()
und diese Konvertierung ist mehrdeutig. Tatsächlich wird der Compiler diese explizite Konvertierung auch nicht akzeptieren - im impliziten Fall nimmt vc++ offenbar diejenige Basis, die in der Vererbungshierarchie am weitesten links steht (nicht ausgiebig getestet) - das ist aber ein Compiler-Bug/Feature.
-
camper schrieb:
...Das ist etwas völlig anderes: hier wird die Funktion A::f durch B::f verdeckt. Der Compiler beschwert sich völlig zu recht....
Darauf wollte ich ja auch nur hinweisen. Mir ging es auch nicht um das konkrete Beispiel, sondern um Michaels Aussage im Generellen.
Gruß,
Simon2.
-
Hallo,
ich habe noch etwas sehr seltsammes festgestellt, was eigentlich auch nicht gehen dürfte (oder doch??)
Mit der MS Visual c++ 2005 Express Edition
Warum kann ich in der main Methode folgendes kompilieren und FEHLERFREI (ohne Absturz oder hinweis im Debug oder Release Modus) ausführen?
Fortbewegungsmittel* f= new Schiff(10,15,900); cout << f->sag_was(); Flugzeug* fl1= (Flugzeug*) f; cout <<fl1->ich_befinde_mich()<<"\n";
Als ausgabe erhalte ich:
Meine Position ist X=10 y=15. Ich befinde mich in der Luft
Somit habe ich doch ein "Schiff" zu einem "Flugzeug" gemacht, welche auf der Erbungshirachie sozusagen menschlich ausgedrück Brüder währen.
Füge ich nun in "Fortbewegungsmittel" die Methode "sag_was" rein virtuell ein also:
virtual string ich_befinde_mich()=0;
Dann ändert sich die Ausgabe(Bei gleichem code in der main Methode) in
Meine Position ist X=10 y=15. Ich befinde mich im Wasser
Ich habe vorher JAVA gelernt und versuche mir jetzt c++ beizubringen. Aber ich bin über die Ausgaben doch sehr erstaunt und auch darüber, das das überhaupt ausgeführt werden kann.
Warum geht das? (Auch ein BUG oder SOLL das gehen?)
-
Der Fehler in dem Code ist, dass du hier einen C-Style cast benutzt, richtig wäre ein dynamic_cast, dann würde dein Programm zur Laufzeit prüfen ob die Umwandlung legal ist, beim C-Style Cast sagst du "ist schon in Ordnung so, ich will das so haben" und dann macht der Compiler genau das was du da haben willst.
Die unterschiedliche Ausgabe hat nichts zu bedeuten, das ist undefiniertes Verhalten.
-
Simon2 schrieb:
camper schrieb:
...Das ist etwas völlig anderes: hier wird die Funktion A::f durch B::f verdeckt. Der Compiler beschwert sich völlig zu recht....
Darauf wollte ich ja auch nur hinweisen. Mir ging es auch nicht um das konkrete Beispiel, sondern um Michaels Aussage im Generellen.
Die auf das konkrete Beispiel bezogen war ;p
-
Michael E. schrieb:
Simon2 schrieb:
camper schrieb:
...Das ist etwas völlig anderes: hier wird die Funktion A::f durch B::f verdeckt. Der Compiler beschwert sich völlig zu recht....
Darauf wollte ich ja auch nur hinweisen. Mir ging es auch nicht um das konkrete Beispiel, sondern um Michaels Aussage im Generellen.
Die auf das konkrete Beispiel bezogen war ;p
Weiß ich auch ... aber "Generalaussagen" kpnnen durchaus missverstanden werden - besonders von (offensichtlich in Sachen "C++-Namensauflösung" unerfahrenen) Anfängern/Java-Umsteigern.
Es gibt bei diesem Thema einfach eine Menge Mechanismen, die tw. gleichzeitig, teilweise nacheinander ablaufen und ein nicht seltenes Problem ist, dass man sie nicht alle oder ihr Zusammenspiel kennt.Ich wollte einfach nur etwas genauer spezifizieren, was da alles passiert - ist kein persönlicher Angriff gewesen.
Gruß,
Simon2.
-
Simon2 schrieb:
ist kein persönlicher Angriff gewesen.
Is klar
Das hatte ich zu deinen Anfangszeiten ja auch mal falsch verstanden