2033 Stimmen

Regelmäßige Besetzung vs. statische Besetzung vs. dynamische Besetzung

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 natürlich regelmäßige Casts verwendet, d.h.

MyClass *m = (MyClass *)ptr;

überall, aber es scheint zwei andere Arten von Gipsen 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);

78 Stimmen

Ich würde den Cast im alten C-Stil nicht als "regulären Cast" in C++ bezeichnen, da er alles andere als das ist. Man sollte ihn in C++ generell nicht verwenden, vor allem nicht bei Klassen, denn es ist einfach zu einfach, damit Fehler zu machen. Seine Verwendung ist ein Zeichen für einen C-Programmierer, der zu C++ gewechselt ist, aber C++ noch nicht ganz gelernt hat.

136 Stimmen

Wie kann eine Frage mit einer Antwort ein Duplikat einer Frage ohne Antwort sein? mehr noch, diese Frage wurde früher gestellt als das "Original"

7 Stimmen

@Vladp Für den Fall, dass Sie sich immer noch fragen, oder jemand anderes dies liest und sich fragt . (Außerdem war es kein Moderator, der die Seite geschlossen hat, sondern ein Benutzer mit einer dupe-hammer )

1840voto

Johannes Schaub - litb Punkte 479831

Statisch_gecastet

static_cast" wird für Fälle verwendet, in denen man im Grunde eine implizite Konvertierung rückgängig machen will, mit ein paar Einschränkungen und Ergänzungen. `static_cast` führt keine Laufzeitprüfungen durch. Dies sollte verwendet werden, wenn man weiß, dass man sich auf ein Objekt eines bestimmten Typs bezieht und somit eine Prüfung unnötig 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 eine MyClass Objekt, so dass keine Laufzeitprüfung erforderlich ist, um dies sicherzustellen.

dynamisch_gecastet

dynamic_cast" ist nützlich, wenn man den dynamischen Typ des Objekts nicht kennt. Es gibt einen Null-Zeiger zurück, wenn das Objekt, auf das verwiesen wird, den Typ, auf den gecastet wurde, nicht als Basisklasse enthält (wenn Sie auf eine Referenz casten, wird in diesem Fall eine "bad_cast"-Ausnahme geworfen).

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

Sie können nicht verwenden dynamic_cast wenn Sie downcast (auf eine abgeleitete Klasse casten) und der Argumenttyp nicht polymorph ist. Der folgende Code ist zum Beispiel nicht gü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" (Cast zur Basisklasse) ist immer gültig mit beiden static_cast y dynamic_cast und auch ohne Cast, da ein "Up-Cast" eine implizite Konvertierung ist (vorausgesetzt, die Basisklasse ist zugänglich, d. h. es handelt sich um eine public Vererbung).

Reguläre Besetzung

Diese Gussformen werden auch als C-Guss bezeichnet. Ein C-Style-Cast ist im Grunde genommen identisch mit dem Ausprobieren einer Reihe von C++-Cast-Sequenzen und der Wahl des ersten C++-Casts, der funktioniert, ohne jemals zu berücksichtigen dynamic_cast . Dies ist natürlich sehr viel leistungsfähiger, da es alle Aspekte der const_cast , static_cast y reinterpret_cast aber es ist auch unsicher, denn es verwendet keine dynamic_cast .

Darüber hinaus erlauben C-artige Casts nicht nur, dies zu tun, sondern sie erlauben auch, sicher auf eine private Basisklasse zu casten, während die "entsprechende" static_cast Sequenz würde einen Kompilierfehler auslösen.

Manche Leute bevorzugen C-Style Casts wegen ihrer Kürze. Ich verwende sie nur für numerische Casts und verwende die entsprechenden C++-Casts, wenn benutzerdefinierte Typen beteiligt sind, da sie eine strengere Überprüfung bieten.

14 Stimmen

Siehe auch die beiden zusätzlichen Würfe von boost: boost.org/doc/libs/1_47_0/libs/conversion/

3 Stimmen

@JohannesSchaub-litb: Sind Sie sicher, dass ein Cast im C-Stil einen "sicheren" Cast auf eine private Basisklasse ermöglicht? Ich kann mir vorstellen, dass das funktioniert, wenn die private Basisklasse die einzige /Basis/ ist, aber was ist mit virtueller/mehrfacher Vererbung? Ich gehe davon aus, dass der C-Style-Cast keine Zeigermanipulationen vornimmt.

2 Stimmen

@JohannesSchaub-litb ist es wahr, dass es auch einige Overhead bei der Verwendung der alten C-Stil Casts über die C ++ Casts beteiligt ist?

280voto

Hossein Punkte 21448

Statischer Guss

Der statische Cast führt Konvertierungen zwischen kompatiblen Typen durch. Er ähnelt dem Cast im Stil von C, ist aber restriktiver. Der Cast im Stil von C würde es beispielsweise ermöglichen, dass ein Integer-Zeiger auf ein Char zeigt.

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

Da dies zu einem 4-Byte-Zeiger führt, der auf 1 Byte des zugewiesenen Speichers zeigt, führt das Schreiben auf diesen Zeiger entweder zu einem Laufzeitfehler oder überschreibt einen angrenzenden Speicher.

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

Im Gegensatz zum Cast in C erlaubt der statische Cast dem Compiler zu prüfen, ob die Datentypen von Zeiger und Pointee kompatibel sind, so dass 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 Zeigerumwandlung zu erzwingen, so wie es der C-artige Cast im Hintergrund tut, würde stattdessen der reinterpret cast verwendet werden.

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

Dieser Cast dient der Konvertierung zwischen bestimmten nicht verwandten Typen, z. B. von einem Zeigertyp in einen anderen inkompatiblen Zeigertyp. Es wird einfach eine binäre Kopie der Daten durchgeführt, ohne das zugrunde liegende Bitmuster zu verändern. Beachten Sie, dass das Ergebnis einer solchen Low-Level-Operation systemspezifisch und daher nicht portabel ist. Sie sollte mit Vorsicht verwendet werden, wenn sie nicht gänzlich vermieden werden kann.

Dynamische Besetzung

Dieser wird nur verwendet, um Objektzeiger und Objektreferenzen in andere Zeiger- oder Referenztypen in der Vererbungshierarchie umzuwandeln. Es ist der einzige Cast, der sicherstellt, dass das Objekt, auf das er zeigt, konvertiert werden kann, indem er zur Laufzeit überprüft, ob der Zeiger auf ein vollständiges Objekt des Zieltyps verweist. Damit diese Prüfung zur Laufzeit möglich ist, muss das Objekt polymorph sein. Das heißt, die Klasse muss mindestens eine virtuelle Funktion definieren oder erben. Der Grund dafür ist, dass der Compiler die benötigten Laufzeit-Typinformationen nur für solche Objekte erzeugt.

Dynamische Besetzungsbeispiele

Im folgenden Beispiel wird eine MyChild Zeiger wird in eine MyBase Zeiger mit einem dynamischen Cast. Diese abgeleitete Umwandlung in ein Basisobjekt ist erfolgreich, da das Child-Objekt ein vollständiges Base-Objekt 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, eine MyBase Zeiger auf eine MyChild Zeiger. Da das Base-Objekt kein vollständiges Child-Objekt enthält, schlägt diese Zeigerumwandlung fehl. Um dies anzuzeigen, gibt der dynamische Cast einen Null-Zeiger zurück. Auf diese Weise kann zur Laufzeit überprüft werden, 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 der dynamische Cast fehl, indem ein bad_cast Ausnahme. Diese muss mit einer try-catch Erklärung.

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

Dynamischer oder statischer Wurf

Der Vorteil eines dynamischen Casts besteht darin, dass der Programmierer während der Laufzeit prüfen kann, ob eine Konvertierung erfolgreich war oder nicht. Der Nachteil ist, dass diese Prüfung mit einem Leistungs-Overhead verbunden ist. Aus diesem Grund wäre im ersten Beispiel ein statischer Cast vorzuziehen gewesen, da eine abgeleitete Konvertierung in eine Basis nie fehlschlägt.

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

Im zweiten Beispiel kann die Umwandlung jedoch entweder gelingen oder scheitern. Sie wird fehlschlagen, wenn die MyBase Objekt enthält eine MyBase Instanz und wird erfolgreich sein, wenn sie eine MyChild Beispiel. In manchen Situationen kann dies bis zur Laufzeit nicht bekannt sein. In diesem Fall ist ein dynamischer Cast die bessere Wahl als ein statischer Cast.

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

Wäre die Konvertierung von Basis zu abgeleitet mit einem statischen Cast statt einem dynamischen Cast durchgeführt worden, wäre die Konvertierung nicht fehlgeschlagen. Sie hätte einen Zeiger zurückgegeben, der auf ein unvollständiges Objekt verweist. Die Dereferenzierung eines solchen Zeigers kann zu Laufzeitfehlern führen.

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

// Incomplete MyChild object dereferenced
(*child);

Konst Besetzung

Dieser wird in erster Linie verwendet, um die const Modifikator einer Variablen.

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

Obwohl const cast die Änderung des Wertes einer Konstante erlaubt, ist dies dennoch ungültiger Code, der einen Laufzeitfehler verursachen kann. Dies könnte beispielsweise der Fall sein, wenn sich die Konstante in einem Bereich mit schreibgeschütztem Speicher befindet.

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

const cast wird stattdessen hauptsächlich dann verwendet, wenn es eine Funktion gibt, die ein nicht konstantes Zeigerargument annimmt, auch wenn sie den Zeiger nicht verändert.

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

Der Funktion kann dann eine konstante Variable übergeben werden, indem man eine const Besetzung.

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

print(nonConst); // allowed

Quelle und weitere Erklärungen

0 Stimmen

std::bad_cast ist definiert in <typeinfo>

1 Stimmen

Vom Kind zur Basis, ist ein Wurf nicht notwendig: MyBase *base = child; // ok

7 Stimmen

Meiner Meinung nach die beste Antwort, sehr einfach und doch klar

91voto

TJ Seabrooks Punkte 19463

Sie sollten sich den Artikel ansehen _C++ Programmierung/Type Casting_ .

Sie enthält eine gute Beschreibung der verschiedenen Wurfarten. Die folgenden aus dem oben genannten Link genommen:

const_cast

const_cast(expression) Die Funktion const_cast<>() dient zum Hinzufügen/Entfernen von const(ness) (oder volatile-ness) einer Variablen.

statisch_gecastet

static_cast(expression) Die Funktion static_cast<>() wird verwendet, um zwischen den Ganzzahl-Typen. z.B.: char->long, int->short usw.

Der statische Cast wird auch verwendet, um Zeiger auf verwandte Typen zu werfen, zum Beispiel z.B. void* auf den entsprechenden Typ zu übertragen.

dynamisch_gecastet

Der dynamische Cast wird verwendet, um Zeiger und Referenzen zur Laufzeit zu konvertieren, im Allgemeinen, um einen Zeiger oder eine Referenz nach oben oder unten einer Vererbungskette (Vererbungshierarchie).

dynamic_cast(Ausdruck)

Der Zieltyp muss ein Zeiger- oder Referenztyp sein, und die Ausdruck muss zu einem Zeiger oder Verweis ausgewertet werden. Dynamische Besetzung funktioniert nur, wenn der Objekttyp, auf den der Ausdruck verweist, mit dem mit dem Zieltyp kompatibel ist und die Basisklasse über mindestens eine virtuelle Mitgliedsfunktion hat. Wenn dies nicht der Fall ist und der Typ des zu besetzenden Ausdrucks ein Zeiger ist, wird NULL zurückgegeben, wenn ein dynamischer Cast auf eine Referenz fehlschlägt, wird eine bad_cast-Ausnahme geworfen. Wenn es nicht scheitert, gibt der dynamische cast einen Zeiger oder eine Referenz des Zieltyps auf das Objekt zurück zurück, auf das der Ausdruck verwiesen hat.

uminterpretieren_gießen

Reinterpret cast wandelt einfach einen Typ bitweise in einen anderen um. Jeder Zeiger oder ganzzahliger Typ kann mit reinterpret cast in jeden anderen umgewandelt werden, was leicht zu Missbrauch führen kann. Zum Beispiel kann man mit reinterpret cast unsicher einen Integer-Zeiger in einen String-Zeiger umwandeln.

35voto

Jason Baker Punkte 180981

FYI, ich glaube, Bjarne Stroustrup wird zitiert, dass C-Stil Casts zu vermeiden sind und dass Sie static_cast oder dynamic_cast verwenden sollten, wenn überhaupt möglich.

Barne Stroustrup's C++ Stil FAQ

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

14 Stimmen

Ja, weil C++-Casts, die explizit gekennzeichnet und bewusst auf klar definierte Rollen beschränkt sind, "höllischer" sind als ein C-Cast, der einfach blindlings mehrere Cast-Typen ausprobiert, bis er alles funktioniert, ohne Rücksicht auf den Sinn ... der war gut.

0 Stimmen

Wenn Sie seine FAQ lesen, empfiehlt Stroustrup sogar, Güsse ganz zu vermeiden. Der erste Satz in seinem Abschnitt über static_cast: "Casts sind im Allgemeinen am besten zu vermeiden."

0 Stimmen

@BillWeinman in der Praxis lassen sich Würfe nicht ganz vermeiden (und soweit es mich betrifft, lässt die Formulierung "am besten vermeiden" dies zu). Sobald Sie Ihren eigenen Code mit APIs oder verschiedenen APIs verknüpfen, werden Sie häufig mit Situationen konfrontiert, in denen die Typen nicht genau übereinstimmen und Sie auf Casts zurückgreifen müssen. Dies gilt insbesondere für ältere und organisch gewachsene APIs. WinAPI ist ein Paradebeispiel dafür.

28voto

ugasoft Punkte 3552

Vermeiden Sie die Verwendung von C-Style-Gipsen.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X