2010-09-19 14 views

Antwort

80

Um das Gusssystem zu verstehen, müssen Sie in das Objektmodell eintauchen.

Die klassische Darstellung einer einfachen Hierarchie Modell ist Eindämmungs: dass, wenn B aus A leitet dann die B Objekt tatsächlich ein A Subobjekt neben eigenen Attribute enthält.

Bei diesem Modell ist Downcasting eine einfache Zeigermanipulation, die durch einen zur Kompilierungszeit bekannten Offset bekannt ist und vom Speicherlayout B abhängt.

Dies ist, was static_cast tun: eine statische Guss statisch genannt wird, weil die Berechnung von dem, was notwendig ist für die Besetzung zur Compile-Zeit durchgeführt wird, ist es arithmetische oder Conversions Zeiger sein (*).

Allerdings, wenn virtual Vererbung Kicks in Sachen neigen dazu, ein bisschen schwieriger zu werden. Das Hauptproblem ist, dass mit virtual Vererbung alle Unterklassen eine gleiche Instanz des Unterobjekts teilen. Um dies zu tun, B wird einen Zeiger auf eine A, anstelle einer A richtig haben, und die A Basisklasse Objekt wird außerhalb von B instanziiert werden.

Daher ist es zur Kompilierzeit unmöglich, die notwendige Zeigerarithmetik ableiten zu können: Sie hängt vom Laufzeittyp des Objekts ab.

Wenn eine Laufzeitabhängigkeit besteht, benötigen Sie RTTI (RunTime Type Information), und RTTI für Umwandlungen zu verwenden, ist die Aufgabe dynamic_cast.

Zusammengefasst:

  • Kompilierung-gesenkten: static_cast
  • Laufzeit niedergeschlagenen: dynamic_cast

Die anderen beiden sind auch Kompilierung-Casts, aber sie sind so spezifisch, dass es ist leicht sich daran zu erinnern, wofür sie sind ... und sie sind stinkend, also besser, sie überhaupt nicht zu benutzen.

(*) Wie @curiousguy in den Kommentaren erwähnt, gilt dies nur für Downcasting. Ein static_cast ermöglicht Upcasting unabhängig von virtueller oder einfacher Vererbung, obwohl dann der Besetzung auch nicht notwendig ist.

+4

Schöne Antwort, die mich verstehen ließ, wie virtuelle Vererbung tatsächlich funktioniert!+1 – undu

+0

Ich mag Ihre Antwort, aber die OP war offenbar gefragt, einen Fehler für Downcast statt Upcasting. – h9uest

+0

@ h9uest: Danke, dass du auf den Ausrutscher hingewiesen hast, ich habe "upcasting" in "downcasting" geändert und jetzt ist alles in Ordnung. –

11

Soweit ich weiß, müssen Sie dynamic_cast verwenden, weil die Vererbung virtual ist und Sie downcasting sind.

6

Sie können static_cast in dieser Situation nicht verwenden, da der Compiler zu Zeitpunkt der Kompilierung den Offset von B relativ zu A nicht kennt. Der Offset muss zur Laufzeit basierend auf dem genauen Typ des am meisten abgeleiteten Objekts berechnet werden. Daher müssen Sie dynamic_cast verwenden.

+0

Wenn ein abgeleiteter zu einer virtuellen Basis konvertieren, können Sie 'static_cast' verwenden. – curiousguy

+0

@curiousguy: Ja, aber die Frage bezieht sich auf die Umwandlung von Base in abgeleitete. – ybungalobill

+0

Es ist nicht klar, warum Ihr Argument gilt nicht auch für abgeleitet von Base. – curiousguy

4

Ja, Sie müssen ein dynamic_cast verwenden, aber Sie müssen die Basisklasse A polymorph machen, z. durch Hinzufügen eines virtuellen dtor.

+1

oder mindestens eine virtuelle Methode hinzufügen. – Liton

4

Laut Standard docs,

Abschnitt 5.2.9 - 9, für Static Guss,

An rvalue of type “pointer to cv1 B,” where B is a class type, can be converted to an rvalue of type “pointer to cv2 D,” where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.

Daher ist es nicht möglich, und Sie sollten dynamic_cast ...

verwenden
1

$5.2.9/2- "An expression e can be explicitly converted to a type T using a static_cast of the form static_cast(e) if the declaration “T t(e);” is well-formed, for some invented temporary variable t (8.5)."

In Ihrem Code Sie versuchen static_cast mit 'T = B *' und 'e = A *'

Now 'B * t (A *)' ist in C nicht wohlgeformt ++ (aber 'A * t (B *)' ist, weil 'A' eine virtuell eindeutige und zugängliche Basis von 'B' ist. Daher gibt der Code einen Fehler.

+0

Falsches Angebot. – curiousguy

1

Ich weiß nicht, ob das aber "sicher" ist .

Unter der Annahme,

B von A abgeleitet (und eine rein virtuelle)

Da ich, dass ein Zeiger auf B weiß noch bleibt ein Zeiger auf B.

class A 
    { 
      virtual void doSomething(const void* p) const =0; 
    }; 

    class B 
    { 
    public: 
      int value; 
      virtual void doSomething(const void*p)const 
      { 
      const B * other = reinterpret_cast<const B*>(p); 
      cout<<"hello!"<< other->value <<endl; 
      } 
    }; 

    int main() 
    { 
      B foo(1),bar(2); 
      A * p = &foo, q=&bar; 
      p->doSomething(q); 
      return 0; 
    } 

Dieses Programm führt und richtig Druck zurückzukehren „Hallo!“ und der Wert des anderen Objekts (in diesem Fall "2").

übrigens, was ich tue, ist sehr unsicher (persönlich gebe ich jeder Klasse eine andere ID und ich behaupte nach Casting neu interpretieren, dass aktuelle ID gleich anderen ID ist, um sicher zu sein, dass wir etwas mit 2 gleichen tun Klassen) und wie Sie sehen, beschränkte ich mich auf "const" Methoden. Daher wird dies mit "nicht-konstanten" Methoden funktionieren, aber wenn Sie etwas falsch machen, wird der Fehler fast unmöglich sein. Und selbst bei Behauptung gibt es eine 1 Chance von 4 Milliarden Behauptung erfolgreich zu sein, auch wenn es angeblich zum Scheitern verurteilt (behauptet (ID == andere-> ID);)

Durch die Art und Weise .. Ein gutes OO-Design sollte erfordern diese nicht irgendwie von Sachen, aber in meinem Fall habe ich versucht, Refactoring/den Code neu zu gestalten, ohne die Verwendung von reinterpret Gießen fallen zu können. Generell können Sie solche Dinge vermeiden.

+0

Sind Sie sicher, dass dies notwendig ist? –

+0

das ist spezifisch für Ihr Problem. Ein Re-Design sollte dies in den meisten Fällen verhindern (vermeide mein Beispiel, wenn du kannst). Oh Liebes, ich vergesse ein "const". – GameDeveloper

+0

Ich meine, bist du sicher, dass du es brauchst? Was passiert, wenn ich einen int Zeiger gebe? Das würde katastrophal scheitern. Warum nicht einen dynamischen Cast verwenden und das Ergebnis überprüfen? Ich weiß nicht, was Ihre genauen Anforderungen sind, aber ich glaube, wenn Sie das statische Polymorphismus-System (z. B. CRTP-Muster) bringen, können Sie sich etwas Sichereres einfallen lassen. –