Typkonvertierungen
Mithilfe der Typkonvertierung wird der Datentyp eines Ausdrucks in einen anderen Datentyp konvertiert.
Folgende Typkonvertierungen stehen zur Verfügung:
const_cast
static_cast
dynamic_cast
reinterpret_cast
DTYP ist der Datentyp, in den AUSDRUCK konvertiert werden soll. Ob die Konvertierung zulässig ist oder nicht wird zur Compilezeit oder zur Laufzeit überprüft.
Die 'alten' Typkonvertierungen mittels (DTYP) AUSDRUCK bzw. DTYP (AUSDRUCK) sollten nicht mehr verwendet werden, da sie keinerlei Plausibilitätsprüfung enthält.
const_cast() Konvertierung
Die const_cast() Konvertierung entfernt oder fügt den const oder volatile Modifizierer zu einem Datentyp hinzu (volatile wird später erklärt). Dabei darf sich der neue Datentyp vom Ursprungsdatentyp nur durch den Spezifizierer const oder volatile unterscheiden.
// Zeiger auf konstante Zeichen
auto pConst = "Text";
// Zeiger auf veränderbare Zeichen
char *pVar;
// Konvertierung const char* nach char*; Fehler!
pVar = pConst;
// Aber so geht's
pVar = const_cast<char*>(pConst);
// Alternativ:
pVar = const_cast<decltype(pVar)>(pConst);
Wenn Sie von einem Datentyp den const-Modifizierer entfernen, müssen Sie genau wissen was Sie tun! In der Regel führt das Entfernen des const-Modifizierers nicht automatisch dazu, dass die ursprüngliche Konstante verändert werden kann. Auf manchen Systemen, insbesondere Embedded Systemen, liegen Konstanten im ROM (Read-Only Speicher, z.B. Flash oder EEPROM). Und Schreibzugriffe auf einen solchen Speicher können bis zum Programmabsturz führen!
static_cast() Konvertierung
Die static_cast() Konvertierung wird eingesetzt, um einen void-Zeiger in einen beliebigen anderen Zeiger (typisierten Zeiger) zu konvertieren. Ebenfalls eingesetzt wird der static_cast() Operator, um zwischen Integer-Datentypen und Gleitkomma-Datentypen zu konvertieren.
// Konvertierung von long nach char
// Es wird nur das niederwertigste Byte übernommen
long longVar = ..;
char charVar = static_cast<char>(longVar);
// void-Zeiger in int-Zeiger konvertieren
void *pVal = ..;
int *pVar = static_cast<int*>(pVal);
Außerdem kann der static_cast() Operator ein Integer-Datum in einen enum-Wert konvertieren und umgekehrt. Dem enum-Datentyp ist später ein eigenes Kapitel (enum-Datentyp) gewidmet.
Und zu guter Letzt kann der static_cast() Operator dazu verwendet werden, Objektzeiger zu konvertieren deren Klassen in einer Beziehung zueinander stehen (Stichwort: Ableitung). Diese Konvertierung wird im Kapitel Typkonvertierungen und Klassenzeiger behandelt.
reinterpret_cast() Konvertierung
Typkonvertierungen mittels reinterpret_cast() sind die fehlerträchtigsten, da sie in der Regel plattformabhängige Konvertierungen durchführen. Die reinterpret_cast() Konvertierung wird zum einen für die Konvertierung zwischen verschiedenen Zeigertypen und zum anderen für die Konvertierung von Integer-Daten in Zeiger und umgekehrt eingesetzt.
// Definitionen
const char* pText = "Text";
long lVal;
short* sPtr;
// Konvertierungen const char* Zeiger nach long
lVal = reinterpret_cast<long>(pText);
// Konvertierung long* nach short*
sPtr = reinterpret_cast<short*>(&lVal);
dynamic_cast() Konvertierung
Die dynamic_cast() Konvertierung wird später im Anschluss an die Behandlung von abgeleiteten Klassen beschrieben, da sie nur in diesem Zusammenhang von Bedeutung ist (Typkonvertierungen und Klassenzeiger). Sie ist nur der Vollständigkeit wegen an dieser Stelle erwähnt.
Automatische Typkonvertierungen (Promotions)
Da Operationen nur Daten mit gleichen Datentypen verarbeiten, werden viele Konvertierungen durch den Compiler automatisch vorgenommen. Die Anpassung erfolgt in der Weise, dass der 'kleinere' Datentyp auf den 'größeren' erweitert wird. Ausnahme: Bei Zuweisungen wird der Datentyp des rechten Operanden immer an den Datentyp des linken Operanden angepasst.
Ohne auf alle Einzelheiten einzugehen, die im Standard mehrere Seiten füllen, gibt es folgende automatische Konvertierungen.
Integral Promotion
Daten vom Datentyp char, (un-)signed char und (un-)signed short können vom Compiler auf den Datentyp int erweitert werden, wenn dadurch der gesamte Wertebereich des Original-Datentyps abgedeckt wird. Ist dies nicht der Fall, wird eine Anpassung auf unsigned int versucht. Ebenfalls können enum und wchar_t Datentypen nach int bzw. long (sowohl signed als auch unsigned) umgewandelt werden. Und zum Schluss kann ein bool-Datum in einen int-Wert konvertiert werden, wobei für true gleich 1 und für false gleich 0 verwendet wird.
char var = 0x55;
if (var == 0x80) // Konvertierung von char auf int!
bool bVar = true;
int nVar = bVar; // Konvertierung von bool auf int
Alle arithmetischen Operatoren und Shift-Operatoren führen ihre Operation immer mindestens mit dem Datentyp int aus.
signed char a=100, b=3, c=2;
signed char res = a * b / c; // Ergebnis: res=150
Die Berechnung erfolgt hier von links nach rechts, d.h., es wird zuerst a*b berechnet und das Ergebnis daraus durch c geteilt. Würde die Berechnung als signed char Berechnung durchgeführt werden, würde a*b das Ergebnis 44 liefern (0x64*0x03=0x12c, als char Ergebnis 0x2c). Und dies geteilt durch 2 würde dann dezimal 22 ergeben. Tatsächlich liefert die Auswertung des Ausdrucks das erwartete Ergebnis von 150, da die Operanden vor der Operation auf int konvertiert werden.
Floating Promotion
Ein Datum vom Typ float wird bei Bedarf auf ein Datum vom Datentyp double konvertiert.
Floating-Integral Konvertierung
Ein Gleitkommadatum kann in ein Integer-Datum konvertiert werden, wobei die Nachkommastellen abgeschnitten werden.
Zeiger Konvertierungen
Ein Zeiger eines beliebigen Datentyps kann immer in einen void-Zeiger (typloser Zeiger) konvertiert werden. Des Weiteren kann ein Zeiger auf eine abgeleitete Klasse in einen Zeiger auf seine Basisklasse konvertiert werden. Dieser Sachverhalt spielt später eine wichtige Rolle.
Integral Konvertierung mit Rundungen
Obwohl keine Konvertierung im bisherigen Sinn, es thematisch aber hierher passt, stehen zwei Bibliotheksfunktionen für die Konvertierung von Gleitkommawerte in long- oder long long-Werte mit Rundung zur Verfügung.
lerg = std::lround(fval);
llerg = std::llround(fval);
fval ist der zu rundende Gleitkommawert. Werden diese Funktionen eingesetzt, ist die Header-Datei cmath einzubinden.
#include <print>
#include <cmath>
int main ()
{
// 4 Gleitkommakonstanten definieren
const double val1 = 1./3.;
const double val2 = 2./3.;
const float val3 = -1.F/3.F;
const long double val4 = -2.L/3.L;
// 4 Variablen mit den gerundeten Ergebnissen
// der Gleitkomma-Konvertierungen definieren
auto lerg1 = std::lround(val1);
auto lerg2 = std::lround(val2);
auto llerg1 = std::llround(val3);
auto llerg2 = std::llround(val4);
// Ausgabe der gerundeten Variablen
std::println("lround({:.4}): {}",val1,lerg1);
std::println("lround({:.4}): {}",val2,lerg2);
std::println("llround({:.4}): {}",val3,llerg1);
std::println("llround({:.4}): {}",val4,llerg2);
}
lround(0.3333): 0
lround(0.6667): 1
llround(-0.3333): 0
llround(-0.6667): -1