Udostępnij za pośrednictwem


Typ konwersji i typ bezpieczeństwa (C++)

Dokument ten identyfikuje typowych problemów dotyczących konwersji typu i w tym artykule opisano, jak ich uniknąć w kodzie języka C++.

Pisząc program w języku C++, jest ważne, aby zapewnić, że jest to typ palety.Oznacza to, że każdy zmiennej, argument funkcji i funkcja zwraca wartość jest przechowywanie dopuszczalne rodzaj danych, oraz że operacje, które mogą obejmować wartości różnych typów "sensu" i nie spowodować utratę danych, niewłaściwej interpretacji bit desenie lub związanej z uszkodzeniem pamięci.Program, który nigdy nie jawnie lub niejawnie konwertuje wartości z jednego typu na drugi jest typ palety z definicji.Jednak podczas konwersji typów, nawet niebezpieczne konwersje, czasami są wymagane.Na przykład trzeba przechowywać wynik zmiennoprzecinkową punkt operacji w zmiennej typu int, mogą też przenieść wartość niepodpisany int do funkcji, które przekieruje podpisanego przez nią int.Oba przykłady ilustrują konwersje niebezpieczne, ponieważ mogą one powodować utratę danych lub reinterpretacji wartości.

Kompilator wykryje konwersję niebezpieczne, wystawia się błąd lub ostrzeżenie.Błąd zatrzymania tworzenia statystyk; Ostrzeżenie umożliwia kompilację kontynuować, ale wskazuje możliwy błąd w kodzie.Jednak nawet wtedy, gdy program kompiluje się bez ostrzeżenia, nadal może zawierać kod, który prowadzi do niejawne konwersje, które generują niepoprawne wyniki.Błędy typu mogą również wprowadzone przez jawne konwersje lub prezentacji, w kodzie.

Niejawne konwersje typów

Gdy wyrażenie zawiera operandy wbudowanych różnego typu, a nie jawne prezentacji są obecne, kompilator używa wbudowane konwersji standardowych do konwersji, jeden z argumentów, aby odpowiadały typów.Kompilator stara konwersje w sekwencji wyraźnie określone, dopóki jeden powiedzie się.Jeśli wybrane konwersji jest promocja, kompilator nie wystawia ostrzeżenie.Jeśli konwersja jest zwężenie, kompilator generuje ostrzeżenie o możliwości utraty danych.Czy występuje utrata danych rzeczywistych zależy od wartości rzeczywiste, udział, ale zaleca się, że to ostrzeżenie są traktowane jako błąd.Jeśli typ zdefiniowany przez użytkownika jest zaangażowany, kompilator próbuje użyć konwersji, które określono w definicji klasy.Jeśli nie znajdzie dopuszczalne konwersji, kompilator wygenerują błąd i nie kompiluje program.Aby uzyskać więcej informacji na temat reguł, które określają sposób konwersji standardowych, zobacz Konwersji standardowych.Aby uzyskać więcej informacji na temat konwersji zdefiniowane przez użytkownika, zobacz Konwersje zdefiniowane przez użytkownika (C++/CLI).

Hh279667.collapse_all(pl-pl,VS.110).gifPoszerzenie konwersji (promocja)

W poszerzenia konwersji wartość w zmiennej mniejszych przydzielono większych zmiennej bez utraty danych.Ponieważ konwersje poszerzenia są zawsze bezpieczne, kompilator wykonuje je po cichu, a nie wystawia ostrzeżenia.Poniższe konwersje są poszerzenie konwersji.

Od

Aby

Podpisane lub niepodpisane typem całkowitym, z wyjątkiem long long lub__int64

double

bool lub char

Wbudowany typ

short lub wchar_t

int, long, long long

int, long

long long

float

double

Hh279667.collapse_all(pl-pl,VS.110).gifKonwersji zawężającej (przymus)

Kompilator wykonywał konwersji zawężającej niejawnie, ale ostrzega użytkownika o możliwej utracie danych.Podjąć te ostrzeżenia bardzo poważnie.Jeśli masz pewność, że nie dane zostaną utracone, ponieważ wartości w zmiennej większych zawsze mieści się w zmiennej mniejsze, następnie dodać jawne rzutowania tak, że kompilator nie jest już wygeneruje ostrzeżenie.Jeśli nie masz pewności, że konwersja jest bezpieczny, Dodaj do kodu pewnego rodzaju wyboru runtime do obsługi możliwości utraty danych, tak aby nie powoduje programu, aby spowodować uzyskanie niepoprawnych wyników.Aby uzyskać sugestie, jak radzić sobie z tego scenariusza, zobacz Jak: uchwyt zawężanie konwersji (C++).

Przeliczyć zmiennoprzecinkową punktu typ na typ integralny jest konwersji zawężającej, ponieważ część ułamkową liczby zmiennoprzecinkowe wskazuje wartość jest odrzucona i utracone.

Poniższy przykład kodu pokazuje niektóre niejawne zwężenie konwersje i ostrzeżenia, które kompilator generuje dla nich.

   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(pl-pl,VS.110).gifPodpisane – konwersje bez znaku

Podpisane typem całkowitym a jego odpowiednikiem niepodpisane są zawsze taki sam rozmiar, ale różnią się one w sposób wzorzec bitowy jest interpretowana wartość transformacji.Poniższy przykład kodu pokazuje, co się dzieje, gdy ten sam wzorzec bitowy jest interpretowane jako wartość podpisane, a jako wartość bez znaku.Wzorzec bitowy przechowywane w obu num i num2 nigdy się nie zmienia się od przedstawionego na wcześniejszej ilustracji.

   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

Należy zauważyć, że wartości są postrzegane w obu kierunkach.Jeśli Twój program daje wyniki nieparzysta, w których wydaje się znak wartości odwrócony od oczekiwań, poszukaj konwersje niejawne między podpisane i niepodpisane typy zintegrowane.W poniższym przykładzie wynik wyrażenia (0-1) jest niejawnie konwertować z int do unsigned int podczas przechowywania w num.Powoduje to, że wzorzec bitowy do zinterpretowane.

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

Kompilator nie Ostrzegaj o konwersji niejawnych między podpisane i niepodpisane typy zintegrowane.Dlatego zaleca się, aby całkowicie uniknąć podpisane do unsigned konwersji.Jeśli nie można ich uniknąć, następnie dodać do kodu wyboru runtime do wykrywania, czy wartość docelową konwersji jest większa niż lub równa zero i mniejsza lub równa maksymalnej wartości typu podpisane.Wartości w tym zakresie zostaną przeniesione z podpisany bez znaku z lub bez znaku do podpisane, nie są postrzegane.

Hh279667.collapse_all(pl-pl,VS.110).gifWskaźnik konwersji

Wiele wyrażeń tablicy stylu języka C jest konwertowany na wskaźnik do pierwszego elementu w tablicy w stałej konwersji może się tak zdarzyć po cichu.Chociaż jest to wygodne, również jest potencjalnie podatne na błędy.Na przykład w poniższym przykładzie źle zaprojektowany kod wydaje się bezsensowne, a jeszcze skompiluje się w programie Visual C++ i daje w wyniku "p".Po pierwsze, ciąg literału stałej "Help" jest konwertowana na char* wskazujący na pierwszy element tablicy; tego wskaźnika jest zwiększana o trzy elementy, które teraz wskazuje do ostatniego elementu 'p'.

char* s = "Help" + 3;

Konwersje jawne (poświaty)

Za pomocą operacji cast, można nakazać kompilatorowi do konwersji wartości jednego typu do innego typu.Kompilator podniesie błąd w niektórych przypadkach, jeżeli są dwa typy zupełnie niezwiązana z nią, ale zdarza się to nie podniesie błąd nawet jeśli operacja nie jest typu palety.Oszczędne używanie poświaty ponieważ żadnej konwersji z jednego typu do drugiego jest potencjalne źródło błędu programu.Jednak poświaty są czasem potrzebne, a nie wszystkie poświaty są równie niebezpieczne.Jeden efektywnego wykorzystania oddanych jest, gdy kod działa konwersji zawężającej i wiesz, że konwersja nie powoduje programu, aby spowodować uzyskanie niepoprawnych wyników.W efekcie to nakazuje kompilatorowi wiedzieć, co robią i zatrzymania, przeszkadza ci z ostrzeżeniami o tym.Innym zastosowaniem jest do oddania z klasy wskaźnik do pochodnych do klasy wskaźnik do bazy.Innym zastosowaniem jest wyrzucać const- ności zmiennej przekazać je do funkcji, która wymaga non -const argument.Większość tych operacji cast pociąga za sobą pewne ryzyka.

W stylu języka C Programowanie, ten sam operator oddanych stylu języka C służy do wszelkiego rodzaju poświaty.

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

Operator rzutowania stylu języka C jest identyczny (wywołanie operatora) i dlatego rzucają w kodzie i łatwo przeoczyć.Obie są złe, ponieważ są one trudne do rozpoznania na pierwszy rzut oka lub Wyszukaj, a są one wystarczająco zróżnicowane, aby wywołać dowolną kombinacją static, const, i reinterpret_cast.Dowiedzieć się, co się obsadą cyfry faktycznie ma może być trudne i podatne na błędy.Z tych powodów gdy oddanych jest to konieczne, firma Microsoft zaleca zastosowanie jednej z następujących operatorów C++ oddanych, które w niektórych przypadkach są znacznie bardziej typ palety i które znacznie bardziej jawnie express zamiarem programowania:

  • static_cast, odlewy, które są sprawdzane podczas kompilacji tylko raz.static_castZwraca błąd, jeśli kompilator wykryje, że chcesz oddanych między typami, które nie są całkowicie zgodne.Można również użyć, do oddania między wskaźnik do bazy i wskaźnik do pochodnych, ale kompilator nie zawsze może ustalić czy takie przekształcenie będzie bezpieczne w czasie wykonywania.

       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);
    

    Aby uzyskać więcej informacji, zobacz static_cast.

  • dynamic_cast, dla bezpiecznej, sprawdzane w czasie wykonywania odlewy wskaźnik do podstaw do wskaźnika do pochodnych.A dynamic_cast jest bezpieczniejsze niż static_cast downcasts, ale środowisko wykonawcze wyboru pociąga za sobą pewne dodatkowe obciążenie związane.

       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;
    

    Aby uzyskać więcej informacji, zobacz dynamic_cast.

  • const_cast, do odlewania od const- ności zmiennej lub konwertowania non -const zmienna ma być const.Odlewnictwo od const-ności za pomocą tego operatora jest po prostu jako podatne jak używa stylu C oddanych, chyba że z const-cast jest mniej prawdopodobne wykonać oddanych przypadkowo.Czasem trzeba wyrzucać const-ności zmiennej, na przykład, aby przekazać const zmiennej do funkcji, które przekieruje non-const parametru.Poniższy przykład pokazuje, jak to zrobić.

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

    Aby uzyskać więcej informacji, zobacz const_cast.

  • reinterpret_cast, dla rzutowania między niezwiązanym typów, takich jak pointer do int.

    [!UWAGA]

    Tego operatora rzutowania nie jest używany tak często, jak inni, a nie ma gwarancji przenośności inne kompilatory.

    Poniższy przykład pokazuje, jak reinterpret_cast różni się od static_cast.

       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.
    

    Aby uzyskać więcej informacji, zobacz Operator reinterpret_cast.

Zobacz też

Koncepcje

System typów C++ (Podręcznik programowania C++ nowoczesny)

Inne zasoby

Nowoczesne C++ Programming Guide

Skorowidz języka C++

Biblioteka języka C++ wzorcowego