Konwersje standardowe

Język C++ definiuje konwersje między jego podstawowymi typami. Definiuje również konwersje dla typów pochodnych wskaźników, odwołań i wskaźników do składowych. Konwersje te są nazywane konwersjami standardowymi.

W tej sekcji omówiono następujące konwersje standardowe:

  • Promocje całkowite

  • Konwersje całkowite

  • Konwersje zmiennoprzecinkowe

  • Konwersje zmiennoprzecinkowe i całkowite

  • Konwersje arytmetyczne

  • Konwersje wskaźników

  • Konwersje odwołań

  • Konwersje wskaźników do składowych

    Uwaga

    Typy zdefiniowane przez użytkownika mogą określać własne konwersje. Konwersja typów zdefiniowanych przez użytkownika jest omówiona w temacie Konstruktory i konwersje.

Poniższy kod powoduje konwersje (w tym przykładzie promocje całkowite):

long  long_num1, long_num2;
int   int_num;

// int_num promoted to type long prior to assignment.
long_num1 = int_num;

// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;

Wynik konwersji jest wartością l tylko wtedy, gdy generuje typ odwołania. Na przykład konwersja zdefiniowana przez użytkownika zadeklarowana jako operator int&() zwraca odwołanie i jest wartością l. Jednak konwersja zadeklarowana jako operator int() zwraca obiekt i nie jest wartością l.

Promocje całkowite

Obiekty typu całkowitego można przekonwertować na inny szerszy typ całkowity, czyli typ, który może reprezentować większy zestaw wartości. Ten rozszerzający typ konwersji jest nazywany podwyższaniem liczby całkowitej. W przypadku promocji całkowitej można użyć następujących typów w wyrażeniu, gdzie można użyć innego typu całkowitego:

  • Obiekty, literały i stałe typu char i short int

  • Typów wyliczeniowych

  • int pola bitowe

  • Modułów wyliczających

Promocje języka C++ są "zachowujące wartość", ponieważ wartość po podwyższeniu poziomu jest gwarantowana jako taka sama jak wartość przed podwyższeniem poziomu. W promocji zachowujących wartość obiekty krótszych typów całkowitych (takich jak pola bitowe lub obiekty typu ) są promowane do typucharint, jeśli int mogą reprezentować pełny zakres oryginalnego typu. Jeśli int nie można reprezentować pełnego zakresu wartości, obiekt jest promowany do typu unsigned int. Mimo że ta strategia jest taka sama jak ta używana przez standardową C, konwersje zachowujące wartość nie zachowują "podpisu" obiektu.

Zachowujące wartość promocje i promocje, które zachowują znak zazwyczaj generują te same wyniki. Mogą jednak generować różne wyniki, jeśli promowany obiekt będzie wyświetlany jako:

  • Operand z /, %, /=%=, <, <=, , >lub>=

    Operatory te opierają się na znaku w celu określenia wyniku. Promocje zachowujące wartość i zachowujące znaki generują różne wyniki w przypadku zastosowania do tych operandów.

  • Lewy operand lub >>>>=

    Te operatory traktują podpisane i niepodpisane ilości inaczej w operacji przesunięcia. W przypadku podpisanych ilości operacja przesunięcia po prawej stronie propaguje bit logowania do opuszczonych pozycji bitowych, podczas gdy opuszczone pozycje bitowe są zera wypełnione bez znaku ilości.

  • Argument przeciążonej funkcji lub operand przeciążonego operatora, który zależy od podpisu typu operandu do dopasowywania argumentów. Aby uzyskać więcej informacji na temat definiowania przeciążonych operatorów, zobacz Przeciążone operatory.

Konwersje całkowite

Konwersje całkowite to konwersje między typami całkowitymi. Typy całkowite to char, short (lub short int), int, longi long long. Te typy mogą być kwalifikowane za pomocą signed metody lub unsignedi unsigned mogą być używane jako skrót dla elementu unsigned int.

Podpisano do niepodpisanego

Obiekty podpisanych typów całkowitych można konwertować na odpowiadające niepodpisane typy. Po wystąpieniu tych konwersji rzeczywisty wzorzec bitów nie zmienia się. Jednak interpretacja zmian danych. Rozważmy następujący kod:

#include <iostream>

using namespace std;
int main()
{
    short  i = -3;
    unsigned short u;

    cout << (u = i) << "\n";
}
// Output: 65533

W poprzednim przykładzie element , ijest definiowany signed shorti inicjowany do liczby ujemnej. Wyrażenie (u = i) powoduje i przekonwertowanie na element unsigned short przed przypisaniem na u.

Niepodpisane do podpisanego

Obiekty niepodpisanych typów całkowitych można konwertować na odpowiednie typy ze znakiem. Jeśli jednak wartość niepodpisane znajduje się poza reprezentowanym zakresem typu podpisanego, wynik nie będzie miał poprawnej wartości, jak pokazano w poniższym przykładzie:

#include <iostream>

using namespace std;
int main()
{
short  i;
unsigned short u = 65533;

cout << (i = u) << "\n";
}
//Output: -3

W poprzednim przykładzie jest obiektem integralnymunsigned short, u który musi zostać przekonwertowany na liczbę podpisaną w celu obliczenia wyrażenia (i = u). Ponieważ jej wartość nie może być poprawnie reprezentowana w obiekcie signed short, dane są błędnie interpretowane, jak pokazano.

Konwersje zmiennoprzecinkowe

Obiekt typu zmiennoprzecinkowego można bezpiecznie przekonwertować na bardziej precyzyjny typ zmiennoprzecinowy — czyli konwersja nie powoduje utraty istotności. Na przykład konwersje z do lub double z floatdouble do long double są bezpieczne, a wartość jest niezmieniona.

Obiekt typu zmiennoprzecinkowego można również przekonwertować na mniej precyzyjny typ, jeśli znajduje się w zakresie reprezentowanym przez ten typ. (Zobacz Limity zmiennoprzecinkowe dla zakresów typów zmiennoprzecinkowych). Jeśli oryginalna wartość nie jest reprezentowana dokładnie, można ją przekonwertować na następną wyższą lub następną niższą godną reprezentowania wartość. Wynik jest niezdefiniowany, jeśli taka wartość nie istnieje. Rozważmy następujący przykład:

cout << (float)1E300 << endl;

Maksymalna wartość reprezentowana przez typ float to 3,402823466E38, która jest znacznie mniejszą liczbą niż 1E300. W związku z tym liczba jest konwertowana na nieskończoność, a wynik jest "inf".

Konwersje między typami całkowitolicztowymi i zmiennoprzecinkowych

Niektóre wyrażenia mogą powodować konwertowanie obiektów typu zmiennoprzecinkowego na typy całkowite lub odwrotnie. Gdy obiekt typu całkowitego jest konwertowany na typ zmiennoprzecinkowy, a oryginalna wartość nie jest reprezentowana dokładnie, wynik jest następny wyższy lub następna niższa wartość godna reprezentowania.

Gdy obiekt typu zmiennoprzecinkowego jest konwertowany na typ całkowity, część ułamkowa jest obcinana lub zaokrąglona w kierunku zera. Liczba podobna do 1.3 jest konwertowana na 1, a parametr -1.3 jest konwertowany na wartość -1. Jeśli obcięta wartość jest wyższa niż najwyższa godna reprezentowania wartość lub niższa niż najniższa godna reprezentowania wartość, wynik jest niezdefiniowany.

Konwersje arytmetyczne

Wiele operatorów binarnych (omówionych w wyrażeniach z operatorami binarnymi) powoduje konwersje operandów i daje wyniki w taki sam sposób. Konwersje te operatory są nazywane zwykłymi konwersjami arytmetycznymi. Konwersje arytmetyczne operandów, które mają różne typy natywne, są wykonywane, jak pokazano w poniższej tabeli. Typy typedef zachowują się zgodnie z ich podstawowymi typami natywnymi.

Warunki konwersji typów

Spełnione warunki Konwersja
Jeden z operandów ma typ long double. Inny operand jest konwertowany na typ long double.
Poprzedni warunek nie został spełniony, a żaden operand jest typu double. Inny operand jest konwertowany na typ double.
Poprzednie warunki nie zostały spełnione, a żaden operand jest typu float. Inny operand jest konwertowany na typ float.
Powyższe warunki nie są spełnione (żaden z operandów nie jest typu zmiennoprzecinkowego). Operandy otrzymują promocje całkowite w następujący sposób:

- Jeśli którykolwiek operand ma typ unsigned long, drugi operand jest konwertowany na typ unsigned long.
- Jeśli poprzedni warunek nie został spełniony, a jeśli jeden z operandów jest typu long , a drugi typ unsigned int, oba operandy są konwertowane na typ unsigned long.
- Jeśli poprzednie dwa warunki nie są spełnione, a jeśli którykolwiek operand ma typ long, drugi operand jest konwertowany na typ long.
- Jeśli poprzednie trzy warunki nie są spełnione, a jeśli którykolwiek operand ma typ unsigned int, drugi operand jest konwertowany na typ unsigned int.
- Jeśli żaden z powyższych warunków nie zostanie spełniony, oba operandy są konwertowane na typ int.

Następujący kod ilustruje reguły konwersji opisane w tabeli:

double dVal;
float fVal;
int iVal;
unsigned long ulVal;

int main() {
   // iVal converted to unsigned long
   // result of multiplication converted to double
   dVal = iVal * ulVal;

   // ulVal converted to float
   // result of addition converted to double
   dVal = ulVal + fVal;
}

Pierwsza instrukcja w powyższym przykładzie pokazuje mnożenie dwóch typów całkowitych: iVal i ulVal. Warunek spełniony jest taki, że żaden operand nie jest typu zmiennoprzecinkowego, a jeden operand jest typu unsigned int. Dlatego drugi operand, iVal, jest konwertowany na typ unsigned int. Wynik jest następnie przypisywany do dVal. Warunek spełniony w tym miejscu jest taki, że jeden operand ma typ double, więc unsigned int wynik mnożenia jest konwertowany na typ double.

Druga instrukcja w poprzednim przykładzie pokazuje dodanie typu float i całkowitego: fVal i ulVal. Zmienna ulVal jest konwertowana na typ float (trzeci warunek w tabeli). Wynik dodawania jest konwertowany na typ double (drugi warunek w tabeli) i przypisany do dValelementu .

Konwersje wskaźników

Wskaźniki można konwertować podczas przypisywania, inicjowania, porównywania i innych wyrażeń.

Wskaźnik do klas

Istnieją dwa przypadki, w których wskaźnik do klasy można przekonwertować na wskaźnik do klasy bazowej.

Pierwszy przypadek jest wtedy, gdy określona klasa bazowa jest dostępna, a konwersja jest jednoznaczna. Aby uzyskać więcej informacji na temat niejednoznacznych odwołań do klas bazowych, zobacz Wiele klas bazowych.

To, czy klasa bazowa jest dostępna, zależy od rodzaju dziedziczenia używanego w wyprowadzeniu. Rozważ dziedziczenie przedstawione na poniższej ilustracji:

Diagram showing an inheritance graph and base class accessibility.

Na diagramie przedstawiono klasę bazową A. Klasa B dziedziczy z klasy A za pośrednictwem prywatnej chronionej publicznej. Klasa C dziedziczy z B za pośrednictwem publicznej B.

Wykres dziedziczenia ilustrujący ułatwienia dostępu klasy bazowej

W poniższej tabeli przedstawiono dostępność klasy bazowej dla sytuacji przedstawionej na rysunku.

Typ funkcji Pochodne Konwersja z

B* do A* prawnego?
Zewnętrzna (nieozakresowana w klasie) funkcja Prywatne Nie.
Chronione Nie.
Publiczne Tak
Funkcja składowa B (w zakresie B) Prywatne Tak
Chronione Tak
Publiczne Tak
Funkcja składowa języka C (w zakresie C) Prywatne Nie.
Chronione Tak
Publiczne Tak

Drugi przypadek, w którym wskaźnik do klasy można przekonwertować na wskaźnik do klasy bazowej, to użycie jawnej konwersji typu. Aby uzyskać więcej informacji na temat jawnych konwersji typów, zobacz Jawny operator konwersji typów.

Wynikiem takiej konwersji jest wskaźnik do podobiektu, część obiektu, który jest całkowicie opisany przez klasę bazową.

Poniższy kod definiuje dwie klasy i AB, gdzie B pochodzi z Aklasy . (Aby uzyskać więcej informacji na temat dziedziczenia, zobacz Klasy pochodne). Następnie definiuje bObjectobiekt typu B, i dwa wskaźniki (pA i pB) wskazujące obiekt.

// C2039 expected
class A
{
public:
    int AComponent;
    int AMemberFunc();
};

class B : public A
{
public:
    int BComponent;
    int BMemberFunc();
};
int main()
{
   B bObject;
   A *pA = &bObject;
   B *pB = &bObject;

   pA->AMemberFunc();   // OK in class A
   pB->AMemberFunc();   // OK: inherited from class A
   pA->BMemberFunc();   // Error: not in class A
}

Wskaźnik pA jest typu A *, który może być interpretowany jako "wskaźnik do obiektu typu A." bObject Elementy członkowskie (takie jak BComponent i BMemberFunc) są unikatowe dla typu B i dlatego są niedostępne za pośrednictwem .pA Wskaźnik pA umożliwia dostęp tylko do tych cech (funkcji składowych i danych) obiektu zdefiniowanego w klasie A.

Wskaźnik do funkcji

Wskaźnik do funkcji można przekonwertować na typ void *, jeśli typ void * jest wystarczająco duży, aby pomieścić ten wskaźnik.

Wskaźnik do pustki

Wskaźniki do typu void można przekonwertować na wskaźniki do dowolnego innego typu, ale tylko w przypadku rzutowania jawnego typu (w przeciwieństwie do języka C). Wskaźnik do dowolnego typu można przekonwertować niejawnie na wskaźnik na typ void. Wskaźnik do niekompletnego obiektu typu można przekonwertować na wskaźnik void na (niejawnie) i z powrotem (jawnie). Wynik takiej konwersji jest równy wartości oryginalnego wskaźnika. Obiekt jest uznawany za niekompletny, jeśli jest zadeklarowany, ale nie ma wystarczających informacji, aby określić jego rozmiar lub klasę bazową.

Wskaźnik do dowolnego obiektu, który nie const jest lub volatile może być niejawnie przekonwertowany na wskaźnik typu void *.

const i volatile, wskaźniki

Język C++ nie dostarcza standardowej konwersji z const typu lub volatile na typ, który nie const jest lub volatile. Można jednak określić dowolny rodzaj konwersji przy użyciu rzutów typu jawnego (w tym konwersji, które są niebezpieczne).

Uwaga

Wskaźniki języka C++ do elementów członkowskich, z wyjątkiem wskaźników statycznych elementów członkowskich, różnią się od normalnych wskaźników i nie mają tych samych standardowych konwersji. Wskaźniki do statycznych elementów członkowskich są normalnymi wskaźnikami i mają takie same konwersje jak zwykłe wskaźniki.

konwersje wskaźnika null

Całkowite wyrażenie stałe, które oblicza wartość zero lub takie wyrażenie rzutowane na typ wskaźnika, jest konwertowane na wskaźnik nazywany wskaźnikiem null. Ten wskaźnik zawsze porównuje nierówne z wskaźnikiem do dowolnego prawidłowego obiektu lub funkcji. Wyjątkiem są wskaźniki do obiektów opartych, które mogą mieć to samo przesunięcie i nadal wskazywać różne obiekty.

W języku C++11 typ nullptr powinien być preferowany do wskaźnika null w stylu C.

Konwersje wyrażeń wskaźnika

Dowolne wyrażenie z typem tablicy można przekonwertować na wskaźnik tego samego typu. Wynikiem konwersji jest wskaźnik do pierwszego elementu tablicy. W poniższym przykładzie pokazano taką konwersję:

char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].

Wyrażenie, które powoduje zwrócenie funkcji zwracającej określony typ, jest konwertowane na wskaźnik do funkcji zwracającej ten typ, z wyjątkiem sytuacji, gdy:

  • Wyrażenie jest używane jako operand operatora address-of (&).

  • Wyrażenie jest używane jako operand operatora wywołania funkcji.

Konwersje odwołań

Odwołanie do klasy można przekonwertować na odwołanie do klasy bazowej w następujących przypadkach:

  • Określona klasa bazowa jest dostępna.

  • Konwersja jest jednoznaczna. (Aby uzyskać więcej informacji na temat niejednoznacznych odwołań do klas bazowych, zobacz Wiele klas bazowych).

Wynikiem konwersji jest wskaźnik do podobiektu, który reprezentuje klasę bazową.

Wskaźnik do elementu członkowskiego

Wskaźniki do składowych klas można konwertować podczas przypisywania, inicjowania, porównywania i innych wyrażeń. W tej sekcji opisano następujące konwersje wskaźników na składowe:

Wskaźnik do składowej klasy bazowej

Wskaźnik do składowej klasy bazowej można przekonwertować na wskaźnik do składowej klasy pochodnej, gdy spełnione są następujące warunki:

  • Odwrotna konwersja, od wskaźnika do klasy pochodnej do wskaźnika klasy bazowej, jest dostępna.

  • Klasa pochodna nie dziedziczy praktycznie z klasy bazowej.

Gdy lewy operand jest wskaźnikiem do elementu członkowskiego, prawy operand musi być typem wskaźnika do składowej lub stałym wyrażeniem, które daje wartość 0. To przypisanie jest prawidłowe tylko w następujących przypadkach:

  • Prawy operand jest wskaźnikiem do składowej tej samej klasy co lewy operand.

  • Lewy operand jest wskaźnikiem do składowej klasy pochodzącej publicznie i jednoznacznie z klasy prawego operandu.

wskaźnik null do konwersji składowych

Całkowite wyrażenie stałe, które daje w wyniku zero, jest konwertowane na wskaźnik o wartości null. Ten wskaźnik zawsze porównuje nierówne z wskaźnikiem do dowolnego prawidłowego obiektu lub funkcji. Wyjątkiem są wskaźniki do obiektów opartych, które mogą mieć to samo przesunięcie i nadal wskazywać różne obiekty.

Poniższy kod ilustruje definicję wskaźnika do składowej i w klasie A. Wskaźnik , paijest inicjowany do 0, który jest wskaźnikiem null.

class A
{
public:
int i;
};

int A::*pai = 0;

int main()
{
}

Zobacz też

Dokumentacja języka C++