Funktioniert dynamic_cast wirklich für die mehrfache Vererbung?
Ich wollte sehen, ob es möglich ist die Schaffung von "Schnittstellen", Erben Sie, und überprüfen Sie dann, zur Laufzeit, wenn irgendwelche random-Klasse, die diese Schnittstelle implementiert. Dies ist, was ich habe:
struct GameObject {
int x,y;
std::string name;
virtual void blah() { };
};
struct Airholder {
int oxygen;
int nitrogen;
};
struct Turf : public GameObject, public Airholder {
Turf() : GameObject() {
name = "Turf";
}
void blah() { };
};
void remove_air(GameObject* o) {
Airholder* a = dynamic_cast<Airholder*>(o);
if(!a) return;
a->oxygen = 0;
a->nitrogen = 0;
};
Nun, es funktioniert. Die Dokumentation sagt, dass es funktioniert, ist das test-Beispiel funktioniert.. Aber auch, es hat nicht kompiliert, bis ich fügte hinzu, eine virtuelle Methode zu GameObject. Die Sache ist die, ich weiß wirklich nicht, ob das feature verwendet werden soll, wie, dass. Was mich wundert ist die Tatsache, dass ich die Deklaration einer virtuellen Funktion für die Klasse bin ich der überprüfung. Aber natürlich, da ist keiner, der Klasse bin ich die Prüfung selbst hat keine virtuellen Funktionen, in der Tat meine ganze code hat nichts mit virtuellen Funktionen, es ist eine ganz andere Herangehensweise.
So, ich denke, meine Frage ist: Wenn das, was ich Tue wirklich funktioniert, warum brauche ich eine virtuelle Funktion zu geben, meine Klasse eine vtable? Warum kann ich nicht erklären, die Klasse "runtime-Typ" oder so etwas ohne virtuelle Funktionen?
- Ich verstehe, dass dies möglicherweise eher ein learning-übung für Sie, aber können Sie eine Schnittstelle erstellen, die nur rein virtuelle Funktionen und gehen von dort aus.
- Die Dokumentation sagte, es funktioniert ohne eine virtuelle Funktion? Und warum sagen Sie das test-Beispiel funktionierte, wenn man nicht einmal kompilieren?
- Auch wenn Airholder hatte, virtuelle Funktionen, würde ich noch nicht in der Lage sein, die überprüfungen durchführen. Ich glaube, du bist nicht das problem zu verstehen.
- Ich glaube, ich verstehe. Mein Punkt ist, dass dies zu einer komplizierten Lösung. Ich weiß ein wenig über dynamic_cast und RTTI und all das Zeug, aber haben immer fern blieben, weil es schien nicht die beste Lösung für meine Probleme. Vielleicht war ich geworfen von Ihrem Beispiel ist einfacher als das, was Sie wirklich arbeiten. Meine wirkliche Punkt ist zu bevorzugen statische typüberprüfungen und Polymorphismus über alle runtime-Sachen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
[BEARBEITEN] Nach die Kommentare (Menschen Weise klüger als mich) meine Antwort ist komplett falsch. Allerdings machen Sie Ihre Destruktoren virtuelle sowieso. [/EDIT]
In C++ halte ich upcasting zu einem Basis-Typ ist nur sicher, wenn der Destruktor virtuell ist. Technisch ist es sicher, aber in Wirklichkeit, Sie will fast immer einen virtuellen Destruktor. Zum Beispiel:
In diesem Beispiel, wenn obj geht out of scope, die auto_ptr automatisch die Base ' s destructor aber nicht nennen, die Abgeleitet deconstructor, weil der Typ ist ein Ausgangspunkt, nicht eine Abgeleitete. [Edit: Korrekturen] Dies bewirkt Undefiniertes Verhalten (am besten, es verursacht einen Speicherverlust). Ich habe keine Ahnung, warum C++ nicht erforderlich, einen virtuellen Destruktor zu kompilieren herunter wirft, es eigentlich sollte.
dynamic_cast
ist, dass einige member-Funktion nicht haben, werden virtuelle. Wenn dort keine ist ein konformer compiler ablehnen (muss ablehnen!) die Besetzung als illegal.struct b {}; struct d : b {} x; (b&)d;
. In der Tat, die einzige Gefahr, der nicht mit einem virtuellen Destruktor ist, dass, wie in deinem Beispiel, löschen der durch eine Basisklasse undefiniert sind.dynamic_cast
vollkommen legitim, ohne einen virtuellen Destruktor.§ 5.2.7 der Norm sagt:
T. T ist ein Zeiger oder ein Verweis auf eine komplette Klasse Typ, oder "Zeiger auf cv void". Arten werden nicht
definiert ein dynamic_cast. Der dynamic_cast-operator darf nicht weggeworfen werden, constness (5.2.11).
Typ T. Wenn T ein Referenztyp, v ist ein lvalue einer kompletten Klasse Typ, und das Ergebnis ist ein lvalue von
die Art bezeichnet, die von T.
Beschreibung), oder ist es das gleiche wie R mit Ausnahme, dass die Klasse object type R ist mehr cv-qualifiziert als die Klasse
Objekt-Typ in v, das Ergebnis ist v (ggf. konvertiert).
Wenn T "Zeiger auf cv1 B" und " v hat den Typ "Zeiger auf cv2 D", so dass B eine Basisklasse von D, das Ergebnis ist ein
Zeiger auf die einzigartigen B sub-Objekt D Objekt verweist v. Ähnlich, wenn T "Verweis auf cv1 B"
und v hat Typ "cv2 D", so dass B eine Basisklasse von D, das Ergebnis ist ein lvalue, die für die unique60) B sub-Objekt
der D-Objekt bezeichnet von v. sowohl In der Zeiger-und Referenz-Fällen, cv1 gilt die gleiche cvqualification
wie, oder größer cv-Qualifizierung als, cv2, und B ist eine zugängliche eindeutige Basisklasse
von D. [Beispiel:
struct B {};
struct D : B {};
void foo(D* dp)
{
B* bp = dynamic_cast(dp); //äquivalent zu B* bp = dp;
}
—Ende Beispiel]
Und zu machen, eine Art polymorph, braucht es eine virtuelle Funktion, gemäß § 10.3:
Also der Grund warum "ist, weil die Norm so sagt." Das hat auch nicht wirklich sagen, Sie warum der standard sagt ja aber, aber die anderen Antworten, die Abdeckung, die auch, denke ich.
Das Vorhandensein einer virtuellen Funktion ist eine polymorphe Klasse in C++.
dynamic_cast<>
funktioniert nur mit polymorphen Klassen. (Der compiler lehnt eine dynamische Typumwandlung auf eine nicht-polymorphe Objekt.)Polymorphismus Kosten, sowohl in Zeit und Raum (Speicher). Aufrufe virtueller Funktionen sind jetzt indirekt, in der Regel implementiert, um eine virtuelle Tabelle. In einigen kritischen stellen, die Kosten sind einfach nicht hinnehmbar. Also die Sprache die Mittel zur Vermeidung dieser Kosten.
Ähnliche Konzepte existieren anderswo in der Sprache. Das zugrunde liegende Prinzip ist, dass, wenn Sie nicht wollen, verwenden einige high-falutin' - Funktion, die Sie sollten nicht haben zu zahlen für die Tatsache, die einige Leute wollen, es zu benutzen.
dynamic_cast
erfordert, der Typ, polymorphe, und ohne virtuelle Methoden (oder zumindest einen virtuellen Destruktor) einer Art ist nicht (run-time) polymorph. Die einfache Vererbung ist nicht genug. Die run-time-type-information verwendet, die vondynamic_cast
gespeichert ist neben der vtable-wenn mich richtig erinnere.dynamic_cast
erfordert, der Typ, der polymorph": Nur für downcasting.dynamic_cast<parent_class*>(derived_class_pointer)
funktioniert einwandfrei. RTTI ist nicht erforderlich für upcasting; der code für eine solche Besetzung zur Kompilierzeit bekannt ist. Siehe Seth ' s Antwort; er zitierte Kapitel und vers, die (zumindest teilweise). Die Semantik vondynamic_cast
erfordert nicht polymorphe Klassen bis Absatz 6.Gibt es zwei Hauptgründe. Die erste ist, dass es gibt einfach keine Verwendung für Sie. Den Punkt der Vererbung virtuelle Funktionen. Wenn Sie nicht mit virtuellen Funktionen, nicht die Vererbung verwenden.
Das zweite ist, dass es sehr Komplex, um Sie auch tatsächlich umzusetzen
dynamic_cast
das funktioniert ohne virtuelle Funktionen durch den C++ compilation model. Der einzige Weg, um realistisch zu implementierendynamic_cast
ist für den Betrieb auf virtuellen Tisch - blob von Daten, wird typeless. Könntest du eine Klasse definieren und dann nurdynamic_cast
es in einem TU - jetzt eine TU denkt, dass die Klasse eine vtable und nicht. Das wäre sofortige schlecht. So dassdynamic_cast
auf Klassen, die nicht bereits über virtuelle Funktionen wäre gut,export
", das bedeutet "sehr schwer umzusetzen".boost::optional
ist für. Es gibt viele andere Möglichkeiten der Gestaltung optionale Daten-Mitglieder als Vererbung. Aber der zweite Punkt ist viel wichtiger als die erste -, dass es grundsätzlich unmöglich.Wie schon andere gesagt haben, müssen Sie mindestens eine virtuelle Funktion, um eine polymorphe Klasse. Warum das wichtig ist, ist, dass dynamic_cast selbst ist eine polymorphe operation! Gegeben ein base class pointer ist, gibt es unterschiedliche Ergebnisse auf der Grundlage der tatsächlichen Objekt es aufgerufen wird.
C++ ist ein "nicht für das bezahlen, was Sie nicht brauchen" - Philosophie, also die vtable (oder was auch immer Mechanismus, den der compiler verwendet) ist nicht vorgesehen, es sei denn, es ist eine Notwendigkeit, bestimmt durch die Präsenz einer virtuellen Funktion. Offenbar die Entwickler von C++ gedacht, dies sei eine vernünftige Forderung für den ordnungsgemäßen Betrieb der dynamic_cast oder Sie würde haben zur Verfügung gestellt einen Weg zu erzeugen, eine vtable, ohne es.