Type Abfrage
-
Hallo,
ich habe mein Programm auf folgendes reduziert:
class Base { virtual void meth() = 0; }; class Derived1 : public Base { void meth() {} }; class Derived2 : public Base { void meth() {} }; int main (Base* inst) { // Abfrage, ob inst eine Instanz von Derived1 oder Derived2 ist }Wie kann ich die Abfrage umsetzen?
-
class Base { virtual void meth() = 0; virtual was() {}; }; class Derived1 : public Base { void meth() {} void was() {cout<<"Derived1";} }; class Derived2 : public Base { void meth() {} void was() {cout<<"Derived2";} }; int main (Base* inst) { Base *p_base; p_base = new Derived2; p_base->was(); }
-
http://www.cplusplus.com/reference/std/typeinfo/type_info/
oder dynamic_cast
Aber erklär uns doch mal was du machen willst.
-
Das ist nicht so leicht in einem Satz erklärt, was ich machen will. Kurz gesagt, ich erstelle innerhalb der Laufzeit durch eine Factory Instanzen meiner Derived Klassen. Um die Klassen jedoch zu handeln habe ich ein "Interface" (hier: class Base) erstellt. Aber zu einem gewissen Zeitpunkt muss ich jedoch wieder wissen, ob es nun ein Derived1 oder Derived2 ist...
Mir ist aufgefallen das meine gewünschte Abfrage zu weit geht. Mir reicht an der Stelle, ob ich weiß, ob die inst eine Instanz von Derived1 ist.
Der Link klingt ja erstmal ganz interessant, aber wie kann ich das genau auf mein Bsp. übertragen?
int main (Base* inst) { // um typeid anwenden zu können brauche ich eine Instanz zum vergleichen: Derived1 test = (Derived1) inst; // durch diesen cast kommt es aber im schlimmsten Fall zum Absturz!!! }
-
Base *bPtr = new Derived1(); Derived1* dPtr = dynamic_cast< Base* >( bPtr ); if( dPtr != NULL ) { //bPtr hat auf ein Objekt vom Typ Derived1 gezeigt. }
-
int f(Base* inst) { Derived1* test = dynamic_cast<Derived1*>(inst); if (test){ /* ja das war ein Derived1 */ } }Wenn Dervied1 eine Sonderbehandlung benötigt, ist das oft ein Zeichen dafür, dass Dervied1 gar kein Base ist, und damit gar nicht von Base erben sollte. Bevor man sowas macht, sollte man immer nochmal ganz genau darüber nachdenken, was man da eigentlich macht.
-
types schrieb:
int main(Base* inst) { // Abfrage, ob inst eine Instanz von Derived1 oder Derived2 ist }Wie kann ich die Abfrage umsetzen?
main sollte entweder so
int main()oder so:
int main(int argc, char* argv[])aussehen.
Im Regelfall deutet die Notwendigkeit einer solchen Abfrage (ob inst auf ein Derived1 oder auf ein Derived2 Objekt zeigt) auf ein Fehldesign hin. Du solltest versuchen, vom Design her, diese Situationen zu minimieren.
Trotzdem lässt Dich C++ so etwas prüfen. Alle Bedingungen dazu sind erfüllt (nämlich, dass Base "polymorph" ist; denn sie hat mindestens eine virtuelle Funktion). In diesem Fall könntest dynamic_cast verwenden...
Derived1* p = dynamic_cast<Derived1*>(inst); if (p!=0) { ...hat geklappt... der dynamische Typ von *inst ist Derived1 oder von Derived1 oeffentlich abgeleitet... }dynamic_cast und static_cast sind relativ sicher, wenn es um Konvertierungen zwischen Zeigern geht. Wenn die Typen überhaupt nicht zueinander passen, wird es schon eine Fehlermeldung zur Compile-Zeit geben. Die Typen müssen also mindestens "verwandt" sein (bzgl Vererbung). Zusätzlich garantiert dir dynamic_cast, dass, sofern die Konvertierung erfolgreich war (kein Nullzeiger zurück gekommen ist) der resultierende Zeiger wirklich auf ein Objekt des entsprechenden Typs zeigt. "dynamic" heißt der deswegen, weil zur Laufzeit eine Überprüfung stattfindet und ggf ein Nullzeiger rauskommt. "static" dagegen heißt, dass der Programmierer dem Compiler zur Compile-Zeit sagt, dass er es besser weiß und der Compiler kein Laufzeit-Check einzubauen braucht. Jeden dieser Casts kannst Du auch mit Referenzen durchführen, wobei im Falle von dynamic_cast dann eine Ausnahme vom Typ std::bad_cast geworfen wird, statt eine "Null-Referenz" (welche es nicht gibt, ohne undefiniertes Verhalten hervorzurufen) zurück zu geben.
Den dynamischen Typ kannst Du auch über den typeid-Operator erfragen. Allerdings musst Du bei folgendem Code...
if (typeid(*inst)==typeid(Derived1)) { ...der dynamische Typ von *inst ist Derived1... }dran denken, dass, wenn inst auf ein Objekt einer Klasse zeigt, welche nur von Derived1 ableitet, Derived1 nicht mehr der dynamische Typ ist.
Du solltest solche Fallunterscheidungen aber vom Design her vermeiden. Ganz schlimm wird es, wenn Du solche Unterscheidungen an mehreren Stellen machen musst. Fügst Du dann eine neue Klasse der Hierachie zu, musst du auch all Deine Fallunterscheidungen wieder anpassen. Das wäre gar nicht im Sinne von OOP.
kk
types schrieb:
int main (Base* inst) { // um typeid anwenden zu können brauche ich eine Instanz zum vergleichen: Derived1 test = (Derived1) inst; // durch diesen cast kommt es aber im schlimmsten Fall zum Absturz!!! }Das lässt sich gar nicht erst kompilieren. Und diese C-Style-Cast-Syntax solltest Du gleich bleiben lassen. Gewöhn Dir an, präzise auszudrücken, was Du eigentlich willst. In C++ bedeutet das die Verwendung von static_cast und dynamic_cast. Es gibt noch zwei weitere Casts -- const_cast, reinterpret_cast -- aber die sollten in "normalem" Code nicht oder nur sehr selten auftauchen. Ein C-Style-Cast kann alles mögliche sein, static_cast, const_cast, reinterpret_cast oder eine Kombination aus diesen. Da const_cast und reinterpret_cast relativ gefährlich sind und Du mit einem C-Style-Cast diese versehentlich nutzen könntest, solltest Du das mit den C-Style-Casts sein lassen.
-
Erstmal vielen Dank für die vielen Antworten! Die Sonderbehandlung gilt nicht nur für Derived1, sondern an anderer Stelle auch wiederum für Derived2 usw.
Ich verstehe allerdings nicht, warum Base unbedingt polymorph sein muss?! Was ist, wenn dies nicht der Fall ist? Welche Alternativen habe ich da?
-
Der dynamic_cast funktioniert nur mit Klassen, die auch virtuelle Funktionen haben. Wenn du Derived1 ..2 usw. über einen Base* verwendest, ist das aber mit Sicherheit der Fall. Wenn nicht (wobei ich mir da im Moment kaum einen sinnvollen Anwendungsfall vorstellen kann), dann musst du dir die Typerkennung selber einbauen (ohne eine virtuelle Funktion...).
class Base { public: Base(TypeInfoTyp); TypeInfoTyp ichBin; }; class Derived1 : public Base { public: Derived1() : Base(Derived1Type) {} };Das ist dann aber wahrscheinlich ein noch sinnloseres Design als es eh schon ist.
Und nochmal: die Sonderbehandlung für jeden konkreten Typen solltest du unbedingt nochmal überdenken. Du machst dir alle Vorteile, die dir die polymorphe Behandlung der Typen bringt wieder kaputt.
Wenn es unbedingt notwendig ist, dann würde ich statt einer Typeswitch a laif dynamic_cast<X> ... elsif dynamic_cast<Y> ...das Visitor Pattern in Erwägung ziehen. Das ist in den meisten Fällen etwas eleganter, ändert aber eigentlich nichts an der Tatsache, dass du da vermutlich eine Designschwäche hast.
Wenn Du möchtest, kannst du ja mal deinen konkreten Anwendungsfall schildern.
-
An einer anderen Stelle sollte der dynamic cast ganz gut passen. Es handelts sich hier um abstrakte Klassen...
Doch leider spuckt mein Compiler eine Warnung aus:
warning C4541: 'dynamic_cast' für polymorphen Typ 'IXRDependencyObject' mit /GR- verwendet; unvorhersehbares Verhalten möglichWas ist /GR- und was heißt damit dieser Fehler?
-
Sorry, mein Aufruf sieht folgendermaßen aus:
IXRDependencyPropertyPtr inst; //diess Klasse ist abstrakt GetItem(&inst); // externe Funktion, die eine IXRDependencyProperty zurückgibt MyClass* elem = dynamic_cast<MyClass*>(inst); //MyClass ist auch abstrakt if (elem!=0) { // do something }
-
Wahrscheinlich hast du in den Einstellungen RTTI (Runtime Type Information) deaktiviert. Das ist notwendig, damit
typeidunddynamic_castfunktionieren.Aber verwende unbedingt virtuelle Methoden, sofern das möglich ist.
dynamic_castundtypeidsolltest du nur in Ausnahmefällen einsetzen, denn explizite Typunterscheidungen widersprechen dem Polymorphiegedanken und bringen einige Probleme mit sich.