Freigeben über


Typkonvertierungen und Typsicherheit modernes (C++)

Dieses Dokument identifiziert allgemeine Typkonvertierungsprobleme und beschreibt, wie Sie sie im C++-Code vermeiden können.

Wenn Sie C++-Programm schreiben, ist es wichtig, um sicherzustellen, dass es typsicher ist.Dies bedeutet, dass jede Variable, Funktionsargument und den Rückgabewert eine zulässige Art Daten gespeichert und dass Operationen, die Werte verschiedener Typen "betreffen, sinnvoll" sind und nicht Datenverlust, falsche Interpretation von Bitmustern oder Speicherschäden verursachen.Ein Programm das niemals explizit oder implizit konvertiert Werte von einem Typ in einen anderen ist typsicher definitionsgemäß.Allerdings sind Typkonvertierungen, sogar unsichere Konvertierungen, manchmal erforderlich.Möglicherweise müssen Sie das Ergebnis eines Gleitkommavorgangs in einer Variablen des Typs int speichern, oder den Wert in int ohne Vorzeichen an eine Funktion übergeben, die int mit Vorzeichen akzeptiert.Beide Beispiele veranschaulichen unsichere Konvertierungen, da sie möglicherweise Datenverluste oder Neuinterpretation eines Werts verursachen.

Wenn der Compiler eine unsichere Konvertierung erkennt, wird entweder einen Fehler oder eine Warnung aus.Ein Fehler beendet Kompilierung; Warnung ermöglicht eine Kompilierung, um einen möglichen Fehler im Code fortzusetzen jedoch anzugeben.Auch wenn das Programm ohne Warnungen kompiliert, enthält sie möglicherweise weiterhin Code, auf den die implizite Typkonvertierungen führt, die falsche Ergebnisse.Typfehler können durch explizite Konvertierungen oder Umwandlungen, auch im Code eingegeben werden.

Implizite Typkonvertierungen

Wenn ein Ausdruck Operanden unterschiedlicher integrierten Datentypen und enthält keine explizite Umwandlungen vorhanden sind, verwendet der Compiler integrierte Standardkonvertierungen, um einen der Operanden zu konvertieren, damit die Typen entsprechen.Der Compiler versucht die Konvertierungen in einer klar definierte Sequenz, bis eine folgt.Wenn die ausgewählte Konvertierung eine Erweiterung ist, gibt der Compiler keine Warnung.Wenn die Konvertierung ein Eingrenzen ist, gibt der Compiler eine Warnung zu möglichen Datenverlust aus.Ob tatsächlicher Datenverlust auftritt, hängt von den beteiligten tatsächlichen Werten ab, jedoch empfohlen, dass Sie diese Warnung als Fehler behandeln.Wenn ein benutzerdefinierter Typ betroffen ist, versucht der Compiler, die Konvertierungen zu verwenden, die Sie in der Klassendefinition angegeben haben.Wenn sie eine zulässige Konvertierung nicht finden kann, gibt der Compiler einen Fehler aus und nicht kompiliert das Programm.Weitere Informationen zu den Regeln, die die Standardkonvertierungen steuern, finden Sie unter Standardkonvertierungen.Weitere Informationen zu benutzerdefinierten Konvertierungen, finden Sie unter Benutzerdefinierte Konvertierungen (C++/CLI).

Hh279667.collapse_all(de-de,VS.110).gifErweiterungskonvertierungen (Erweiterung)

In einer erweiternden Konvertierung wird ein Wert in einer kleineren Variable zu einer größeren Variable ohne Datenverlust zugewiesen.Da erweiternde Konvertierungen immer sicher sind, führt der Compiler sie automatisch aus und gibt keine Warnungen aus.Die folgenden Konvertierungen sind Erweiterungskonvertierungen.

Von

To

Ein beliebiger ganzzahliger Typ mit oder ohne Vorzeichen schließen long long oder __int64 aus

double

bool oder char

Ein beliebiger anderer integrierter Typ

short oder wchar_t

int, long, long long

int, long

long long

float

double

Hh279667.collapse_all(de-de,VS.110).gifEinschränkende Konvertierungen (Umwandlung)

Der Compiler führt einschränkende Konvertierungen implizit aus, aber er warnt Sie über Daten verloren gehen.Nehmen Sie diese Warnungen sehr ernst.Wenn Sie sicher sind, dass kein Datenverlust auftritt, da die Werte in der größeren Variable immer in kleinere Variable passen, fügen Sie eine explizite Umwandlung hinzu, damit der Compiler nicht mehr eine Warnung ausgibt.Wenn Sie nicht sicher sind, dass die Konvertierung sicher ist, fügen Sie dem Code eine Art von Laufzeitüberprüfung beliebigen Datenverlust des Handles hinzu, damit es nicht das Programm bewirkt, falsche Ergebnisse zu erzeugen.Empfehlungen zum, wie dieses Szenario, finden Sie unter Gewusst wie: Handle-einschränkende Konvertierungen (C++) behandelt.

Jede Konvertierung von einem Gleitkommatyp zu einem ganzzahligen Typ ist eine einschränkende Konvertierung, da der Sekundenbruchteile Teil des Gleitkommawerts verworfen und verloren gehen.

Das folgende Codebeispiel zeigt einige implizite einschränkende Konvertierungen und Warnungen dass die Compilerprobleme dafür an.

   int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
   wchar_t wch = 'A'; //OK
   char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
                 // to 'char', possible loss of data
   unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
                              // 'int' to 'unsigned char'
   int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
                 // 'int', possible loss of data
   int k = 7.7; // warning C4244:'initializing':conversion from 'double' to
                // 'int', possible loss of data

Hh279667.collapse_all(de-de,VS.110).gifMit Vorzeichen - Konvertierungen ohne Vorzeichen

Ein ganzzahliger Typ mit Vorzeichen und deren Entsprechung ohne Vorzeichen sind immer die gleiche Größe, sie unterscheiden sich jedoch in, wie das Bitmuster für Wertstransformation interpretiert wird.Im folgenden Codebeispiel wird veranschaulicht, was geschieht, wenn das gleiche Bitmuster während ein Wert mit Vorzeichen und als Wert ohne Vorzeichen interpretiert wird.Das Bitmuster, das im - num und in num2 ändert gespeichert wird nie, von, was in der vorherigen Abbildung.

   using namespace std;
   unsigned short num = numeric_limits<unsigned short>::max(); // #include <limits>
   short num2 = num;
   cout << "unsigned val = " << num << " signed val = " << num2 << endl;
   // Prints: unsigned val = 65535 signed val = -1

   // Go the other way.
   num2 = -1;
   num = num2;
   cout << "unsigned val = " << num << " signed val = " << num2 << endl;
   // Prints: unsigned val = 65535 signed val = -1

Beachten Sie, dass Werte in beide Richtungen reinterpreted.Wenn das Programm ungerade Ergebnissen, in denen das Vorzeichen des Werts umgekehrt wird von, die erwartete, suchen Sie nach impliziten Konvertierungen zwischen und ganzzahligen Typen signierten ohne Vorzeichen.Im folgenden Beispiel das Ergebnis des Ausdrucks (0 - 1) wird implizit von int zu unsigned int konvertiert, wenn es in num gespeichert hat.Dadurch wird das Bitmuster reinterpreted.

   unsigned int u3 = 0 - 1;
   cout << u3 << endl; // prints 4294967295

Der Compiler eine nicht zur impliziten Konvertierungen zwischen und ganzzahligen Typen signierten ohne Vorzeichen.Daher wird empfohlen, Konvertierungen signierte-zu-ohneVorzeichen verzichten.Wenn Sie diese nicht vermeiden lässt, fügen Sie dem Code eine zu erkennen Ablaufzeit hinzuüberprüfung, ob der Wert, der konvertiert wird, größer oder gleich null und kleiner oder gleich dem maximalen Wert des Typs mit Vorzeichen zu.Werte in diesem Bereich übertragen von mit Vorzeichen zu von ohne Vorzeichen oder ohne Vorzeichen in mit Vorzeichen, ohne reinterpreted.

Hh279667.collapse_all(de-de,VS.110).gifZeigerkonvertierungen

In vielen Ausdrücke wird als Array im implizit in einen Zeiger auf das erste Element im Array konvertiert, und konstante Konvertierungen können automatisch geschehen.Obwohl dies sinnvoll ist, ist auch möglicherweise fehleranfällig.Beispielsweise wird das folgende Verwendung entworfene Codebeispiel sinnlos, bei kompiliert in Visual C++ und erzeugt ein Ergebnis "p".Zuerst wird das konstante Literal "der Hilfe" Zeichenfolge zu char* konvertiert, das zum ersten Element des Arrays zeigt; dieser Zeiger wird durch drei Elemente erhöht, damit sie sich jetzt zum letzten Element "p" zeigt.

char* s = "Help" + 3;

Explizite Konvertierungen (Umwandlungen)

Indem Sie einen Umwandlungsvorgang verwenden, können Sie den Compiler anweisen, um einen Wert von einem Typ in einen anderen Typ zu konvertieren.Der Compiler löst einen Fehler in einigen Fällen aus, wenn die beiden Typen vollständig nicht verknüpft sind, jedoch in anderen Fällen löst kein Fehler aus, wenn der Vorgang nicht typsicher ist.Verwendungsumwandlungen sparsam, da eine Konvertierung von einem Typ in einen anderen eine potenzielle Quelle des Programmfehlers ist.Allerdings sind Umwandlungen manchmal erforderlich, und nicht alle Typumwandlungen sind gleichmäßig gefährlich.Eine effektive Verwendung einer Umwandlung ist, wenn der Code eine einschränkende Konvertierung ausgeführt wird und Sie wissen, dass die Konvertierung nicht das Programm bewirkt, falsche Ergebnisse zu erzeugen.Tatsächlich dieses teilt dem Compiler, dass Sie wissen, was Sie und mit Warnungen zu, es zu beeinträchtigen beendet ist.Eine andere Verwendung ist, einer Zeiger-zu-abgeleiteten Klasse zu einer Zeiger-zuBasisklasse umzuwandeln.Eine andere Verwendung ist das Umwandeln der const-heit einer Variablen, um sie einer Funktion übergeben, die ein Nicht-const-Argument benötigt.Die meisten dieser Umwandlungsvorgänge umfassen ein Risiko mit ein.

Programmierung in der im C-Format wird der gleiche Umwandlungsoperator im C-Format für alle Arten von Umwandlungen verwendet.

(int) x; // old-style cast, old-style syntax
int(x); // old-style cast, functional syntax

Der Umwandlungsoperator im C-Format entspricht dem Aufrufsoperator () identisch und ist daher diskret im Code und leicht übersehen.Beide sind ungültig, da sie schwierig, auf einen Blick zu erkennen oder Suche für befinden, und sie sind genug unvereinbar, jede Kombination von static, von const und von reinterpret_cast aufzurufen.Herauszufinden, was eine alte Umwandlung tatsächlich der Fall ist, kann kompliziert und fehleranfällig sein.Für alle diese Gründe wenn eine Umwandlung erforderlich ist, empfiehlt es sich, einen der folgenden C++-Umwandlungsoperatoren verwenden, die in einigen Fällen typsicherer sind und die viel explizit mit der Programmierung Absicht anzeigen:

  • static_cast, für Umwandlungen, die zur Kompilierzeit nur überprüft werden.static_cast gibt einen Fehler zurück, wenn der Compiler erkennt, dass Sie versuchen, zwischen Typen umzuwandeln, die vollständig nicht kompatibel sind.Sie können sie auch verwenden, um zwischen Zeiger-zuBasis umzuwandeln und Zeiger-zu-abgeleitet, der Compiler kann jedoch nicht immer mitteilen, ob solche Konvertierungen zur Laufzeit sicher sind.

       double d = 1.58947;
       int i = d;  // warning C4244 possible loss of data
       int j = static_cast<int>(d);       // No warning.
       string s = static_cast<string>(d); // Error C2440:cannot convert from
                                          // double to std:string
    
       // No error but not necessarily safe.
       Base* b = new Base();
       Derived* d2 = static_cast<Derived*>(b);
    

    Weitere Informationen finden Sie unter static_cast.

  • dynamic_cast, für als sicher, Laufzeit-überprüfte Umwandlungen der Zeiger-zuBasis zu Zeiger-zu-abgeleitetem.dynamic_cast ist sicherer als static_cast für Umwandlungen, die Laufzeitüberprüfung verursacht verursacht.

       Base* b = new Base();
    
       // Run-time check to determine whether b is actually a Derived*
       Derived* d3 = dynamic_cast<Derived*>(b);
    
       // If b was originally a Derived*, then d3 is a valid pointer.
       if(d3)
       {
          // Safe to call Derived method.
          cout << d3->DoSomethingMore() << endl;
       }
       else
       {
          // Run-time check failed.
          cout << "d3 is null" << endl;
       }
    
       //Output: d3 is null;
    

    Weitere Informationen finden Sie unter dynamic_cast.

  • const_cast zum Umwandeln der const-heit einer Variablen oder Konvertieren einer Nicht-const-Variablen, um const zu sein.Das Umwandeln der const-heit, mithilfe dieses Operators ist nur so fehleranfällig, wie eine C-Typumwandlung verwendet, außer dass mit const-cast sind es weniger wahrscheinlich, die Umwandlung versehentlich auszuführen.Manchmal müssen Sie die const-heit einer Variable umwandeln, beispielsweise um eine const-Variable in einer Funktion zu übergeben, die einen Nicht-const-Parameter akzeptiert.Das folgende Beispiel zeigt, wie Sie dies durchführen:

       void Func(double& d) { ... }
       void ConstCast()
       {
          const double pi = 3.14;
          Func(const_cast<double&>(pi)); //No error.
       }
    

    Weitere Informationen finden Sie unter const_cast.

  • reinterpret_cast, für Umwandlungen zwischen nicht verknüpften Typen wie pointer zu int.

    HinweisHinweis

    Dieser Umwandlungsoperator wird so oft nicht wie die anderen verwendet, und er ist nicht gewährleistet, dass zu anderen Compilern übertragbar.

    Im folgenden Beispiel wird veranschaulicht, wie reinterpret_cast von static_cast unterscheidet.

       const char* str = "hello";
       int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot
                                     // convert from 'const char *' to 'int'
       int j = (int)str; // C-style cast. Did the programmer really intend
                         // to do this?
       int k = reinterpret_cast<int>(str);// Programming intent is clear.
                                          // However, it is not 64-bit safe.
    

    Weitere Informationen finden Sie unter reinterpret_cast Operator.

Siehe auch

Konzepte

C++-Typsystem modernes (C++)

Weitere Ressourcen

Willkommen zurück in C++ modernes (C++)

C++-Sprachreferenz

C++-Standardbibliothek-Referenz