Frage

Auf diese Frage gibt es hier bereits eine Antwort:

Ich schreibe seit fast zwanzig Jahren C- und C++-Code, aber es gibt einen Aspekt dieser Sprachen, den ich nie wirklich verstanden habe.Ich habe offensichtlich normale Abgüsse verwendet, d. h.

MyClass *m = (MyClass *)ptr;

überall, aber es scheint zwei andere Arten von Besetzungen zu geben, und ich kenne den Unterschied nicht.Was ist der Unterschied zwischen den folgenden Codezeilen?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
War es hilfreich?

Lösung

static_cast

static_cast wird für Fälle verwendet, in denen Sie eine implizite Konvertierung grundsätzlich mit einigen Einschränkungen und Ergänzungen rückgängig machen möchten. static_cast führt keine Laufzeitprüfungen durch.Dies sollte verwendet werden, wenn Sie wissen, dass Sie sich auf ein Objekt eines bestimmten Typs beziehen und eine Überprüfung daher nicht erforderlich wäre.Beispiel:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

In diesem Beispiel wissen Sie, dass Sie a bestanden haben MyClass Objekt, und daher ist keine Laufzeitprüfung erforderlich, um dies sicherzustellen.

dynamischer_cast

dynamic_cast ist nützlich, wenn Sie den dynamischen Typ des Objekts nicht kennen.Es gibt einen Nullzeiger zurück, wenn das Objekt, auf das verwiesen wird, nicht den als Basisklasse umgewandelten Typ enthält (bei der Umwandlung in eine Referenz a bad_cast In diesem Fall wird eine Ausnahme ausgelöst.

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

Sie können es nicht verwenden dynamic_cast wenn Sie ein Downcast durchführen (in eine abgeleitete Klasse umwandeln) und der Argumenttyp nicht polymorph ist.Der folgende Code ist beispielsweise ungültig, weil Base enthält keine virtuelle Funktion:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

Ein „Up-Cast“ (Umwandlung in die Basisklasse) ist bei beiden immer gültig static_cast Und dynamic_cast, und auch ohne Besetzung, da eine „Up-Cast“ eine implizite Konvertierung ist.

Regelmäßige Besetzung

Diese Abgüsse werden auch C-Stil-Abgüsse genannt.Eine Umwandlung im C-Stil ist im Grunde identisch mit dem Ausprobieren einer Reihe von Sequenzen von C++-Umsetzungen und der Übernahme der ersten C++-Umwandlung, die funktioniert, ohne jemals darüber nachzudenken dynamic_cast.Es versteht sich von selbst, dass dies viel leistungsfähiger ist, da es alles vereint const_cast, static_cast Und reinterpret_cast, aber es ist auch unsicher, weil es nicht verwendet wird dynamic_cast.

Darüber hinaus ermöglichen Ihnen Umwandlungen im C-Stil nicht nur dies, sondern auch die sichere Umwandlung in eine private Basisklasse, während das „Äquivalent“ static_cast Die Sequenz würde dafür einen Fehler bei der Kompilierung auslösen.

Manche Leute bevorzugen C-artige Abgüsse wegen ihrer Kürze.Ich verwende sie nur für numerische Umwandlungen und verwende die entsprechenden C++-Umwandlungen, wenn benutzerdefinierte Typen beteiligt sind, da sie eine strengere Prüfung ermöglichen.

Andere Tipps

Statische Besetzung

Die statische Umwandlung führt Konvertierungen zwischen kompatiblen Typen durch.Es ähnelt der Besetzung im C-Stil, ist jedoch restriktiver.Die Umwandlung im C-Stil würde es beispielsweise ermöglichen, dass ein ganzzahliger Zeiger auf ein Zeichen zeigt.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Da dies dazu führt, dass ein 4-Byte-Zeiger auf 1 Byte des zugewiesenen Speichers zeigt, führt das Schreiben auf diesen Zeiger entweder zu einem Laufzeitfehler oder zum Überschreiben eines angrenzenden Speichers.

*p = 5; // run-time error: stack corruption

Im Gegensatz zur Umwandlung im C-Stil ermöglicht die statische Umwandlung dem Compiler, zu überprüfen, ob die Zeiger- und Pointee-Datentypen kompatibel sind, wodurch der Programmierer diese falsche Zeigerzuweisung während der Kompilierung erkennen kann.

int *q = static_cast<int*>(&c); // compile-time error

Besetzung neu interpretieren

Um die Zeigerkonvertierung zu erzwingen, würde auf die gleiche Weise wie bei der Umwandlung im C-Stil im Hintergrund stattdessen die Neuinterpretation verwendet werden.

int *r = reinterpret_cast<int*>(&c); // forced conversion

Diese Umwandlung verarbeitet Konvertierungen zwischen bestimmten nicht verwandten Typen, beispielsweise von einem Zeigertyp in einen anderen inkompatiblen Zeigertyp.Es wird einfach eine binäre Kopie der Daten erstellt, ohne das zugrunde liegende Bitmuster zu ändern.Beachten Sie, dass das Ergebnis einer solchen Low-Level-Operation systemspezifisch und daher nicht portierbar ist.Es sollte mit Vorsicht angewendet werden, wenn es nicht ganz vermieden werden kann.

Dynamische Besetzung

Dieser wird nur zum Konvertieren von Objektzeigern und Objektreferenzen in andere Zeiger- oder Referenztypen in der Vererbungshierarchie verwendet.Es ist die einzige Umwandlung, die sicherstellt, dass das Objekt, auf das gezeigt wird, konvertiert werden kann, indem zur Laufzeit überprüft wird, ob der Zeiger auf ein vollständiges Objekt des Zieltyps verweist.Damit diese Laufzeitprüfung möglich ist, muss das Objekt polymorph sein.Das heißt, die Klasse muss mindestens eine virtuelle Funktion definieren oder erben.Dies liegt daran, dass der Compiler nur die erforderlichen Laufzeittypinformationen für solche Objekte generiert.

Dynamische Besetzungsbeispiele

Im folgenden Beispiel wird ein MyChild-Zeiger mithilfe einer dynamischen Umwandlung in einen MyBase-Zeiger umgewandelt.Diese Konvertierung vom abgeleiteten zum Basisobjekt ist erfolgreich, da das untergeordnete Objekt ein vollständiges Basisobjekt enthält.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

Im nächsten Beispiel wird versucht, einen MyBase-Zeiger in einen MyChild-Zeiger umzuwandeln.Da das Basisobjekt kein vollständiges untergeordnetes Objekt enthält, schlägt diese Zeigerkonvertierung fehl.Um dies anzuzeigen, gibt die dynamische Umwandlung einen Nullzeiger zurück.Dies bietet eine bequeme Möglichkeit, zur Laufzeit zu überprüfen, ob eine Konvertierung erfolgreich war oder nicht.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

Wenn eine Referenz anstelle eines Zeigers konvertiert wird, schlägt die dynamische Umwandlung fehl, indem eine bad_cast-Ausnahme ausgelöst wird.Dies muss mithilfe einer Try-Catch-Anweisung gehandhabt werden.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

Dynamische oder statische Besetzung

Der Vorteil der Verwendung einer dynamischen Umwandlung besteht darin, dass der Programmierer während der Laufzeit überprüfen kann, ob eine Konvertierung erfolgreich war oder nicht.Der Nachteil besteht darin, dass mit der Durchführung dieser Prüfung ein Leistungsaufwand verbunden ist.Aus diesem Grund wäre die Verwendung einer statischen Umwandlung im ersten Beispiel vorzuziehen gewesen, da eine Konvertierung von abgeleiteten in eine Basis niemals fehlschlagen wird.

MyBase *base = static_cast<MyBase*>(child); // ok

Im zweiten Beispiel kann die Konvertierung jedoch entweder erfolgreich sein oder fehlschlagen.Es schlägt fehl, wenn das MyBase-Objekt eine MyBase-Instanz enthält, und ist erfolgreich, wenn es eine MyChild-Instanz enthält.In manchen Situationen ist dies möglicherweise erst zur Laufzeit bekannt.In diesem Fall ist die dynamische Umwandlung eine bessere Wahl als die statische Umwandlung.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

Wenn die Konvertierung von der Basis in eine abgeleitete Konvertierung mithilfe einer statischen Umwandlung anstelle einer dynamischen Umwandlung durchgeführt worden wäre, wäre die Konvertierung nicht fehlgeschlagen.Es hätte einen Zeiger zurückgegeben, der auf ein unvollständiges Objekt verweist.Das Dereferenzieren eines solchen Zeigers kann zu Laufzeitfehlern führen.

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Const-Besetzung

Dieser wird hauptsächlich verwendet, um den const-Modifikator einer Variablen hinzuzufügen oder zu entfernen.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Obwohl const cast die Änderung des Werts einer Konstante zulässt, handelt es sich hierbei immer noch um ungültigen Code, der einen Laufzeitfehler verursachen kann.Dies kann beispielsweise der Fall sein, wenn sich die Konstante in einem Abschnitt des Nur-Lese-Speichers befindet.

*nonConst = 10; // potential run-time error

Const Cast wird stattdessen hauptsächlich verwendet, wenn es eine Funktion gibt, die ein nicht konstantes Zeigerargument akzeptiert, auch wenn sie den Zeiger nicht ändert.

void print(int *p) 
{
   std::cout << *p;
}

Der Funktion kann dann mithilfe einer const-Umwandlung eine konstante Variable übergeben werden.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

Quelle und weitere Erklärungen

Du solltest dir den Artikel ansehen C++-Programmierung/Typumwandlung.

Es enthält eine gute Beschreibung aller verschiedenen Besetzungstypen.Folgendes ist dem obigen Link entnommen:

const_cast

const_cast (Ausdruck) Der const_cast <> () wird verwendet, um const (ness) (oder volatile) einer Variablen hinzuzufügen/zu entfernen.

static_cast

static_cast (Ausdruck) Mit dem static_cast <> () werden zwischen den Ganzzahltypen gegossen.'EG' char-> lang, int-> kurz usw.

Statische Besetzung wird auch verwendet, um Zeiger auf verwandte Typen zu gießen, z. B. void* auf den entsprechenden Typ.

dynamischer_cast

Dynamische Besetzung wird verwendet, um Zeigungen und Referenzen zur Laufzeit zu konvertieren, um einen Zeiger oder Referenz in eine Erbskette (Erbschaftsarchie) nach oben oder unten zu werfen.

Dynamic_cast(Ausdruck)

Der Zieltyp muss ein Zeiger oder einen Referenztyp sein, und der Ausdruck muss an einen Zeiger oder eine Referenz bewertet werden.Dynamic Cast funktioniert nur, wenn der Objekttyp, auf den sich der Ausdruck bezieht, mit dem Zieltyp kompatibel ist und die Basisklasse mindestens eine virtuelle Mitgliedsfunktion hat.Wenn nicht, und die Art des Ausdrucks ist ein Zeiger, wird Null zurückgegeben. Wenn eine dynamische Besetzung auf einer Referenz fehlschlägt, wird eine Bad_cast -Ausnahme ausgelöst.Wenn es nicht fehlschlägt, gibt der dynamische Cast einen Zeiger oder eine Referenz des Zieltyps auf das Objekt zurück, auf das sich der Ausdruck verwiesen.

reinterpret_cast

Beim Reinterpret-Cast wird einfach ein Typ bitweise in einen anderen umgewandelt.Jeder Zeiger oder ein integrierter Typ kann mit neuem Cast an jeden anderen gegossen werden, wodurch leicht Missbrauch ermöglicht werden kann.Zum Beispiel mit neu interpretierender Besetzung könnte sich unsicher einen Ganzzahlzeiger auf einen Streichzeiger werfen.

Zu Ihrer Information: Ich glaube, Bjarne Stroustrup wird mit den Worten zitiert, dass Umwandlungen im C-Stil vermieden werden sollten und dass Sie nach Möglichkeit static_cast oder Dynamic_cast verwenden sollten.

Häufig gestellte Fragen zum C++-Stil von Barne Stroustrup

Nehmen Sie diesen Rat an, was Sie wollen.Ich bin weit davon entfernt, ein C++-Guru zu sein.

Vermeiden Sie die Verwendung von Abgüssen im C-Stil.

Casts im C-Stil sind eine Mischung aus const und reinterpret cast und es ist schwierig, sie in Ihrem Code zu finden und zu ersetzen.Ein C++-Anwendungsprogrammierer sollte eine Umwandlung im C-Stil vermeiden.

C-artige Umwandlungen verschmelzen const_cast, static_cast und reinterpret_cast.

Ich wünschte, C++ hätte keine Umwandlungen im C-Stil.C++-Umsetzungen stechen richtig hervor (wie sie sollten;Casts deuten normalerweise darauf hin, dass man etwas Schlimmes getan hat) und unterscheiden richtig zwischen den verschiedenen Arten der Konvertierung, die Casts durchführen.Sie ermöglichen auch das Schreiben ähnlich aussehender Funktionen, z.boost::lexical_cast, was aus Sicht der Konsistenz ganz nett ist.

dynamic_cast verfügt über eine Laufzeittypprüfung und funktioniert nur mit Referenzen und Zeigern static_cast bietet keine Laufzeittypprüfung.Vollständige Informationen finden Sie im MSDN-Artikel static_cast-Operator.

dynamic_cast Unterstützt nur Zeiger- und Referenztypen.Es kehrt zurück NULL wenn die Umwandlung unmöglich ist, wenn der Typ ein Zeiger ist, oder eine Ausnahme auslöst, wenn der Typ ein Referenztyp ist.Somit, dynamic_cast kann verwendet werden, um zu überprüfen, ob ein Objekt von einem bestimmten Typ ist, static_cast nicht möglich (am Ende erhalten Sie einfach einen ungültigen Wert).

C-artige (und andere) Besetzungen wurden in den anderen Antworten behandelt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top