Was ist die richtige Verwendung für dynamic_cast?
Ich viele Male gesagt habe (und gesehen, die mich in der Praxis), dass die Verwendung von dynamic_cast bedeutet oft schlechtes design, weil es kann und sollte ersetzt werden, mit virtuellen Funktionen.
Betrachten Sie beispielsweise den folgenden code:
class Base{...};
class Derived:public Base{...};
...
Base* createSomeObject(); //Might create a Derived object
...
Base* obj = createSomeObject();
if(dynamic_cast<Derived*>(obj)){
//do stuff in one way
}
else{
//do stuff in some other way
}
Es kann leicht gesehen werden, dass anstelle des Schreibens dynamische casts können wir nur hinzufügen, um eine virtuelle Funktion doStuff()
zu Base
- und re-Implementierung in Derived
.
In diesem Fall, meine Frage ist, warum haben wir dynamic_cast in die Sprache überhaupt? Gibt es ein Beispiel, in dem die Verwendung von dynamic_cast gerechtfertigt ist?
Du musst angemeldet sein, um einen Kommentar abzugeben.
JA. Das ist das, was
virtual
Funktionen sind für.Haben Sie bemerkt, wie
virtual
Funktiondynamic_cast
?Verwendung von
dynamic_cast
in der Regel bedeutet, dass Sie können nicht erreichen Ihr Ziel mit common-interface - (ich.e virtuellen Funktionen), daher müssen Sie es umwandeln, um genau die Art, so zu nennen, das bestimmte member-Funktionen vom Typ base/abgeleitete Klassen.Das Problem mit virtuellen Funktionen ist, dass alle Klassen in der Hierarchie muss haben eine Implementierung oder Abstrakt sein, und das ist definitiv nicht immer das richtige zu tun. Was zum Beispiel, wenn
Base
ist eine Schnittstelle, und in der if, die Sie benötigen, um Zugriff auf die internen details der Implementierung derDerived
? Das ist sicherlich nicht machbar in eine virtuelle Funktion. Darüber hinausdynamic_cast
benötigt wird für beide upcasting und downcasting in bestimmten Mehrfachvererbung Situationen. Und es gibt Grenzen, was getan werden kann, in der virtuelle - Funktionen-zum Beispiel, Vorlagen. Und schließlich, manchmal müssen Sie zum speichern einerDerived*
, nicht nur eine Funktion aufrufen, die auf es.Im wesentlichen, virtuelle Funktionen funktionieren nur in einige Fällen nicht alle von Ihnen.
Ich denke, es gibt zwei Fälle, in denen die Verwendung von dynamic_cast ist eine gültige Sache zu tun. Die erste ist zu überprüfen, ob ein Objekt unterstützt eine Schnittstelle, und das zweite ist, zu durchbrechen Kapselung. Lassen Sie mich erklären die beiden im detail.
Prüfung für eine Schnittstelle
Betrachten Sie die folgende Funktion:
(ITransactionControl wäre eine rein abstrakte Klasse.) In dieser Funktion wollen wir "DoStuff" im Zusammenhang mit einer Transaktion, wenn das Objekt unterstützt die transaktionssemantik. Wenn es nicht, es ist in Ordnung, gehen Sie einfach weiter sowieso.
Nun, wir sicherlich könnte nur hinzufügen virtueller Begin() und Commit () - Methode der Object-Klasse, aber dann jeder Klasse, die sich von Objekt erhält Begin() und Commit () - Methoden, auch wenn Sie keine Ahnung von Transaktionen. Einsatz von virtuellen Methoden in der Basisklasse einfach vergiftet, das seine Oberfläche in diesem Fall. Das obige Beispiel fördert eine bessere Einhaltung der sowohl das Prinzip der einzigen Verantwortung und das interface segregation-Prinzip.
Brechen Kapselung
Dies mag seltsam Beratung man bedenkt, dass dynamic_cast ist in der Regel als schädlich weil es erlaubt Sie zu brechen, - Kapselung. Aber richtig gemacht, kann dies eine völlig sichere und leistungsstarke Technik. Betrachten Sie die folgende Funktion:
Es ist nichts falsch hier. Aber nehmen wir nun an, dass Sie sehen, beginnen Sie zu performance-Problemen im Feld. Nach der Analyse, werden Sie feststellen, dass Ihr Programm ist dafür eine auwful viel Zeit in dieser Funktion. Die push_backs Ergebnis in mehreren Speicherzuordnungen. Noch schlimmer, es stellt sich heraus, dass "iterator" ist fast immer eine "ArrayIterator". Wenn nur Sie waren in der Lage, von dieser Annahme ausgehen, dann wird Ihre performance-Probleme verschwinden würden. Mit dynamic_cast, können Sie genau dies tun:
Wieder, wir könnten hinzufügen einer virtuellen "CopyElements" - Methode, um die IIterator Klasse, aber diese hat die gleichen Nachteile, die ich oben erwähnt. Nämlich, es bläst die Schnittstelle. Es zwingt alle implementors haben eine CopyElements Methode, auch wenn ArrayIterator ist die einzige Klasse, die nicht etwas Interessantes.
Alles, was gesagt, ich empfehle diese Techniken sparsam. dynamic_cast ist nicht frei und ist offen für Missbrauch. (Und ehrlich gesagt, ich habe gesehen, dass es missbraucht, viel mehr oft, als ich gesehen habe es früher auch.) Wenn Sie finden, selbst mit es viel, es ist eine gute Idee, zu betrachten, andere Ansätze.
In der Subklasse können auch andere Methoden, die nicht in der Basisklasse, und das kann nicht Sinn machen im Kontext der anderen Unterklassen. Aber generell sollte man es vermeiden.
Was ist, wenn eine Methode (nennen wir es foo), der erhält BaseClass*, und es ist verbraucht für DerivedClass*.
Wenn ich Schreibe:
und der Aufruf von foo mit x komme ich zu foo (BaseClass "varName"), und nicht foo (DerivedClass varName).
Einer Lösung ist die Verwendung von dynamic_cast und testen Sie es für gegen NULL, und wenn es nicht null, Aufruf von foo mit der gegossenen var und nicht x.
Es ist nicht die meisten Objekt-orientierten situation, aber es passiert, und dynamic_cast kann Ihnen helfen, es (gut, casting ist im Allgemeinen nicht zu Objekt-orientiert).