2012-12-09 10 views
10

Wenn Sie hatten die folgenden:Wie funktioniert dynamic_cast?

class Animal{}; 

class Bird : public Animal{}; 

class Dog : public Animal{}; 

class Penguin : public Bird{}; 

class Poodle : public Dog{}; 

Hat dynamic_cast nur zu überprüfen, ob eine Klasse eine abgeleitete Klasse eines anderen ist, oder wenn eine Klasse ist eine Basisklasse von anderen? Also, wenn ich hatte:

Bird* bird; 
Animal* animal; 

bird = dynamic_cast<Animal*>(bird); 
animal = dynamic_cast<Bird*>(animal); 

bird weist darauf jetzt auf eine Animal Klasse, so dass ich bird->some_function(); verwenden kann, und es wird die Funktion in Animal nennen? Und animal zeigt jetzt auf eine Bird Klasse, also kann ich animal->some_function(); tun und es wird some_function(); in Bird anrufen?

Ich habe versucht herauszufinden, wie die dynamische_cast funktioniert, und die Ressourcen, die ich online gefunden habe, waren nicht die hilfreichsten. Wenn jemand andere Einblicke in die Funktionalität von dynamic_cast und einige Fälle, in denen es nützlich wäre, anbieten könnte, würde ich es sehr zu schätzen wissen.

+1

Sie werfen falsch. "Vogel" und "Tier" sind bereits Hinweise, aber Sie nehmen ihre Adressen. –

+0

@Olaf. Danke für deine Bearbeitung. Vielleicht solltest du deinen Kommentar entfernen. –

Antwort

8

Das Wichtigste beim dynamischen Cast ist, dass es auf polymorphic type angewendet werden soll. Ohne diese dynamische Besetzung wirkt sie wie eine statische Besetzung.

Was ist ein polymorpher Typ? Jede Klasse, die über mindestens eine virtuelle Methode oder einen virtuellen Destruktor oder eine virtuelle Basisklasse verfügt, ist polymorph. Nur diese Typen haben virtual method table (VMT) in ihrem Datenlayout. Klassen, die virtuell nichts haben, haben keine VMTs. Standard sagt nicht, wie Polymorphismen und virtuelle Methoden implementiert werden sollen, gleichzeitig tun dies alle Compiler (AFAIK).

In Ihren Beispielen sind Klassen nicht polymorph. Meiner Meinung nach wäre es besser, wenn Compiler einen Fehler ausgeben würden, wenn der dynamische Cast auf einen nicht-polymorphen Typ angewendet wird. Trotzdem tun sie das nicht. Dies trägt zur Verwirrung bei.

VMT-Zeiger für alle Klassen sind unterschiedlich. Dies bedeutet, dass zur Laufzeit betrachtet:

Animal* animal; 

ist es möglich zu wissen, was die wahre Klasse des Objekts ist. Ist es ein Bird oder ein God oder etwas anderes. Wenn der generierte Code den tatsächlichen Typ aus dem Wert von VMT kennt, kann er eine Anpassung vornehmen, wenn dies erforderlich ist. Hier

ein Beispiel:

class Animal { virtual ~Animal(); int m1; }; 
class Creature { virtual ~Creature(); int m2; }; 

class Bird : public Animal, Creature { }; 

Bird *bird = new Bird(); 
Creature *creature = dynamic_cast<Creature*>(bird); 

Beachten Sie, dass Kreatur nicht die erste Basisklasse ist. Dies bedeutet, dass der Zeiger verschoben wird, um auf den rechten Teil des Objekts zu zeigen. Dennoch wird im Folgenden noch arbeiten:

Animal *animal = dynamic_cast<Animal*>(creature); // Case2. 

weil VMT von Kreatur, wenn es Teil der anderen Klasse ist nicht das gleiche des Objekts zu VMT sein, wenn es Stand-alone-Einsatzes:

Creature *creature1 = new Creature(); 

Diese Unterscheidung ermöglicht die korrekte Implementierung von dynamischem Cast. Im Beispiel Case2 wird der Zeiger zurück verschoben. Ich habe das getestet. Das funktioniert.

2

Das macht nicht viel Sinn, wie Sie es sagen.

Der Punkt von dynamic_cast ist, um Polymorphismus zur Laufzeit aufzulösen.So dass das tatsächliche interessantes Szenario wäre so etwas wie

void animalhandler(Animal& animal); 

sein, die jedoch nicht (zumindest nicht nur) mit Instanzen der Animal genannt, aber mit einem der Unterklassen. Sie müssen oft nicht einmal wissen: Sie können virtuelle Mitglieder von animal aufrufen und sicherstellen, dass C++ die richtige Überladung aufruft, für welche abgeleitete Klasse auch immer *animal gehört.

Aber manchmal möchten Sie etwas tun, das nur mit einer bestimmten abgeleiteten Instanz möglich ist. In diesem Fall verwenden Sie dynamic_cast, wie

void animalhandler(Animal& animal) { 
    if(auto as_bird = dynamic_cast<Bird*>(&animal)) { 
    // bird-specific code 
    } 
} 

wo das if nur Feuer, wenn animal in der Tat eines Bird (oder abgeleitet von Bird), da sonst die dynamic_cast nur nullptr zurück, welche die if interpretiert als false.

Jetzt kommen Sie auf die Idee, das Gegenteil zu tun. Mal sehen, wie das aussehen würde:

if(auto as_bird = dynamic_cast<Bird*>(&animal)) { 
    if(auto as_animal = dynamic_cast<Animal*>(as_bird)) { 
     // animal-specific code 
    } 
    } 

... warte, bedeutet es etwas tierspezifisch zu sein? Nein, weil alleBird s sind Animal s, wir wissen, dass bei der Kompilierung-Zeit gibt es keinen Punkt, es dynamisch zu überprüfen. Sie können es immer noch schreiben, aber Sie können es auch weglassen und as_bird direkt verwenden, da es Zugang zu allen Mitgliedern gibt, die as_animal würde.

3

Der Operator dynamic_cast prüft den Typ des tatsächlichen Objekts, auf das der Zeiger zeigt. Dies unterscheidet sich von der Kompilierungszeit static_cast; Das Ergebnis von dynamic_cast hängt von den Laufzeitdaten ab.

dynamic_cast<Animal*>(bird) 

Im obigen Fall, Animal ist eine übergeordnete Klasse von Bird so dynamic_cast hier nicht notwendig ist (und der Compiler die gleiche wie ein static_cast oder ohne Gussüberhaupt behandeln wird).

dynamic_cast<Bird*>(animal) 

In diesem Fall, wenn diese Aussage tatsächlich ausgeführt wird, wird das Laufzeitsystem den tatsächlichen Typen, gleich welche Art von Objekt inspiziert animal tatsächlich verweist. Es könnte sich um eine Bird oder eine Unterklasse von Bird handeln, in welchem ​​Fall das Ergebnis ein gültiger sein wird. Wenn das Objekt kein Bird ist, wird das Ergebnis NULL sein.

Ihre Frage wird durch die Tatsache komplizierter, dass Sie das Ergebnis dieser dynamic_cast Aufrufe dem ursprünglichen Zeiger zurück zuweisen. Dies ist vielleicht, wo ein Teil der Verwirrung herkommt, und ich habe diesen Aspekt aus der obigen Diskussion weggelassen.

0

Ich hoffe, das hilft:

#include <iostream> 
#include <algorithm> 
#include <vector> 
#include <utility> 

using namespace std; 

class A{ 
public: 
    A(){} 
    virtual void write() const = 0; 
}; 

class B : public A{ 
public: 
    B(){} 
    void write() const { cout << "I'm B" << endl; } 
    void iam(){ cout << "Yes, I am" << endl; } 
}; 

int main(){ 
    B b; 

    A* a; 
    a = &b; 

    b.write(); 
    b.iam(); 

    a->write(); 
    //a->iam(); A don't have a method iam 


    system("pause"); 
    return 0; 
} 
0

Aus dem C++ Working Draft

Dynamische Guss [expr.dynamic.gegossen]

1 Das Ergebnis des Ausdrucks dynamic_cast < > T (v) ist das Ergebnis des Ausdrucks v Umwandlung T. T eingeben wird ein Zeiger oder eine Referenz zu einem kompletten Klassentyp oder „Zeiger auf cv sein Leere“. der dynamic_cast Betreiber hat nicht Konstantheit (5.2.11) wegzuwerfen.

6 Andernfalls v wird ein Zeiger auf ein L-Wert oder einer polymorphen Typ (10.3) sein.

8 Wenn die C ist Klassentyp, auf den T verweist oder verweist, wird die Laufzeitprüfung logisch wie folgt ausgeführt:
- Wenn im abgeleiteten Objekt (r everred) zu v, v verweist (verweist) auf ein Subobjekt der öffentlichen Basisklasse eines C-Objekts, und wenn nur ein Objekt des Typs C vom Subobjekt abgeleitet wird, auf das v verweist (verweist), so verweist das Ergebnis auf diesen C-Objekt.
- Andernfalls, wenn v auf ein Unterobjekt der öffentlichen Basisklasse des am meisten abgeleiteten Objekts verweist (verweist) und der Typ des am meisten abgeleiteten Objekts eine Basisklasse vom Typ C hat, die eindeutig und öffentlich ist, werden die Ergebnispunkte (verweist auf das C-Unterobjekt des am weitesten abgeleiteten Objekts.
- Andernfalls schlägt die Laufzeitprüfung fehl.

Was können Sie von diesen Klauseln

  • dynamic_cast Arbeiten mit polymorphen Klassen schließen
  • sieht es bei Laufzeit auf das Objekt gerichtet (oder bezeichnet) zu
  • es auf Basis entscheidet public Basisklassen des Objekts, auf das gezeigt wird, ob der Cast erfolgreich ist oder fehlschlägt
Verwandte Themen