Was Typumwandlung ist
Typumwandlung bedeutet, einen Wert von einem Typ in einen anderen zu konvertieren - ein double in ein int, ein char in seinen numerischen Code oder einen Zeiger auf eine Basisklasse in einen auf eine abgeleitete Klasse. C++ nimmt einige dieser Konvertierungen automatisch für dich vor, aber genau die, die es still erledigt, sind die Stelle, an der sich Bugs verstecken.
Es gibt zwei Varianten: implizite Konvertierungen, die von selbst geschehen, und explizite Casts, die du selbst hinschreibst. Ein Symptom davon hast du bereits bei den Operatoren kennengelernt - die Ganzzahldivision. Casting ist die Art, wie du die Kontrolle darüber übernimmst.
Implizite Konvertierungen
Wenn du in einem Ausdruck Zahlentypen mischst, befördert C++ den "kleineren" Typ zum "größeren", damit beide Seiten zusammenpassen. Das tut meist genau das, was du willst.
Die Probleme beginnen, wenn die Konvertierung in die andere Richtung geht - von einem breiteren zu einem schmaleren Typ. Das ist eine einschränkende Konvertierung, und sie kann still Daten verlieren.
Von float zu int wird in Richtung null abgeschnitten - es wird nicht gerundet, also wird aus 3.99 3. Und 300 in ein char zu zwängen führt zu einem Überlauf. Viele Compiler warnen hier; manche nicht. Wenn du wirklich einschränken willst, sag es explizit mit einem Cast, damit der nächste Leser weiß, dass es Absicht war.
Die Falle der Ganzzahldivision beheben
Der mit Abstand häufigste Grund für einen Cast ist die Division. Wenn beide Operanden Ganzzahlen sind, führt / eine Ganzzahldivision durch und wirft den Rest weg.
Die Lösung ist static_cast<double> auf einen Operanden vor der Division. Ein häufiger Fehler ist static_cast<double>(got / total) - das ist zu spät, denn got / total ist bereits 0, wenn der Cast ausgeführt wird, also bekommst du 0.0. Wandle einen Operanden um, nicht das Ergebnis.
static_cast: dein Standard-Cast
C++ gibt dir vier benannte Casts. Den, den du in 95 % der Fälle verwenden wirst, ist static_cast<T>(value), der wohldefinierte Konvertierungen zwischen verwandten Typen durchführt - numerische Konvertierungen, von enum zu int, von void* zurück zu einem typisierten Zeiger und das Auf-/Absteigen in einer Klassenhierarchie, wenn du den Typ bereits kennst.
Bevorzuge static_cast gegenüber dem alten Cast im C-Stil (int)balance. Ein Cast im C-Stil versucht jede Konvertierung, um den Code kompilieren zu lassen - einschließlich der gefährlichen weiter unten - sodass er still const entfernen oder rohe Bytes neu interpretieren kann. static_cast erlaubt nur Konvertierungen, die der Compiler tatsächlich rechtfertigen kann, und das ausführliche static_cast<...> lässt sich in einem Code-Review mühelos finden.
// Vermeiden - Cast im C-Stil, kein Sicherheitsnetz:
int dollars = (int) balance;
// Bevorzugen - explizit, geprüft, auffindbar:
int dollars = static_cast<int>(balance);
Die anderen drei Casts (sparsam verwenden)
Die übrigen Casts existieren für bestimmte, eng umrissene Aufgaben. Greife nur dann zu ihnen, wenn static_cast es wirklich nicht kann.
const_cast entfernt (oder fügt hinzu) const. Seine einzige legitime Verwendung ist der Aufruf einer API im C-Stil, die vergessen hat, einen Parameter als const zu markieren. Ein Objekt, das ursprünglich als const deklariert wurde, über einen const_cast zu verändern, ist undefiniertes Verhalten.
void legacyApi(char* msg); // alte API, nimmt kein const
const char* text = "hello";
legacyApi(const_cast<char*>(text)); // nur in Ordnung, wenn legacyApi nicht hineinschreibt
reinterpret_cast interpretiert das rohe Bitmuster neu - z. B. einen Zeiger als ganzzahlige Adresse. Er führt keinerlei Konvertierung durch und ist äußerst unsicher; fast immer ein Zeichen dafür, dass du das Design überdenken solltest.
dynamic_cast konvertiert einen Zeiger oder eine Referenz auf eine Basisklasse sicher zur Laufzeit in einen abgeleiteten Typ, wobei der tatsächliche Typ des Objekts verwendet wird. Er erfordert eine polymorphe Basis (eine Klasse mit mindestens einer virtual-Funktion) und gibt nullptr zurück, wenn der Cast nicht zutrifft.
Hätte a auf ein anderes Animal gezeigt, würde dynamic_cast<Dog*> nullptr zurückgeben und der else-Zweig würde ausgeführt - genau deshalb ist er sicherer, als blind static_cast zu verwenden, um in einer Hierarchie nach unten zu gehen.
Häufige Fehler, die du vermeiden solltest
- Das Ergebnis statt eines Operanden umwandeln.
static_cast<double>(a / b)verwirft zuerst deinen Nachkommateil. Wandleaoderbum. - Annehmen, dass von float zu int gerundet wird. Es wird abgeschnitten:
static_cast<int>(2.99)ist2. Zum Runden verwendestd::round,std::lroundusw. - Zu einem Cast im C-Stil greifen. Er verbirgt, welche Konvertierung stattfindet. Verwende
static_cast, und du bekommst einen Kompilierfehler, wenn die Konvertierung unsicher ist, statt einer stillen Überraschung. - In einen zu kleinen Typ einschränken.
300in eincharoder ein riesigeslongin einintumzuwandeln, bricht um oder läuft über. Wähle einen Zieltyp, der breit genug für den Bereich ist.
Weiter: If-Else
Jetzt, da du Werte sauber konvertieren und vergleichen kannst, ist der nächste Schritt, mit ihnen Entscheidungen zu treffen. Die if-else-Anweisung führt je nachdem, ob eine Bedingung true ist, unterschiedlichen Code aus - die Grundlage jedes verzweigenden Programms.
Häufig gestellte Fragen
Was ist der Unterschied zwischen static_cast und einem Cast im C-Stil in C++?
Ein Cast im C-Stil wie (int)x probiert nacheinander jede Konvertierung durch - er kann sich still in einen gefährlichen reinterpret_cast verwandeln oder const entfernen. static_cast<int>(x) führt nur verwandte Konvertierungen aus, die der Compiler überprüfen kann, sodass der Compiler Unsinn ablehnt. In modernem C++ solltest du immer static_cast gegenüber Casts im C-Stil bevorzugen; er ist sicherer und weitaus leichter mit grep zu finden.
Wie wandle ich in C++ ein int in ein double um?
Verwende static_cast<double>(x). Das ist vor allem bei der Division wichtig: 5 / 2 ist Ganzzahldivision und ergibt 2, aber static_cast<double>(5) / 2 ergibt 2.5. Wandle einen der Operanden um, bevor die Division stattfindet - das Ergebnis umzuwandeln, static_cast<double>(5 / 2), ist zu spät und ergibt immer noch 2.0.
Warum ergibt das Umwandeln eines großen Werts in einen kleineren Typ in C++ eine falsche Zahl?
Die Umwandlung in einen Typ, der den Wert nicht aufnehmen kann, ist eine einschränkende Konvertierung. Von float zu int wird der Nachkommateil abgeschnitten (static_cast<int>(3.99) ist 3), und eine Ganzzahl außerhalb des Bereichs wird entweder umgebrochen (vorzeichenlos) oder ist implementierungsdefiniert (vorzeichenbehaftet). Der Compiler hält dich meist nicht auf, also wandle bewusst um und stelle sicher, dass der Zieltyp breit genug ist.