Szukaj…


Wprowadzenie

Wyrażenie można jawnie przekonwertować lub rzutować na typ T za pomocą static_cast<T> dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> const_cast<T> lub const_cast<T> , w zależności od zamierzonego typu rzutowania.

C ++ obsługuje również notację rzutowania w stylu funkcyjnym, T(expr) i notację rzutowania w stylu C, (T)expr .

Składnia

  • specyfikator typu prostego ( )
  • specyfikator typu prostego ( lista wyrażeń )
  • specyfikator typu prostego braced-init-list
  • typename-specifier ( )
  • typename-specifier ( lista-wyrażeń )
  • typename-specifier braced-init-list
  • dynamic_cast < type-id > ( wyrażenie )
  • static_cast < typ-id > ( wyrażenie )
  • reinterpret_cast < typ-id > ( wyrażenie )
  • const_cast < typ-id > ( wyrażenie )
  • ( typ-id ) rzutowane wyrażenie

Uwagi

Wszystkie sześć notacji rzutowych ma jedną wspólną cechę:

  • Rzutowanie na typ odwołania do wartości, jak w dynamic_cast<Derived&>(base) , daje wartość. Dlatego jeśli chcesz zrobić coś z tym samym obiektem, ale traktować go jako inny typ, możesz przerzucić na typ odwołania do wartości.
  • Rzutowanie na typ odwołania do wartości, jak w static_cast<string&&>(s) , daje wartość.
  • Rzutowanie na typ niereferencyjny, jak w (int)x , daje wartość, którą można uznać za kopię rzutowanej wartości, ale z innym typem niż oryginał.

reinterpret_cast kluczowe reinterpret_cast odpowiada za wykonanie dwóch różnych rodzajów „niebezpiecznych” konwersji:

static_cast kluczowe static_cast może wykonywać wiele różnych konwersji:

  • Konwersja do pochodnych

  • Każda konwersja, którą można wykonać przez bezpośrednią inicjalizację, w tym zarówno konwersje niejawne, jak i konwersje, które wywołują jawnego konstruktora lub funkcję konwersji. Zobacz tutaj i tutaj, aby uzyskać więcej informacji.

  • Aby void , co odrzuca wartość wyrażenia.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Między typami arytmetyki i wyliczeń oraz między różnymi typami wyliczeń. Zobacz konwersje enum

  • Od wskaźnika do elementu klasy pochodnej, od wskaźnika do elementu klasy podstawowej. Wskazane typy muszą się zgadzać. Zobacz konwersję pochodną do podstawy dla wskaźników na pręty

  • void* do T* .

C ++ 11

Konwersja bazy do pochodnej

Wskaźnik do klasy bazowej można przekonwertować na wskaźnik do klasy pochodnej za pomocą static_cast . static_cast nie wykonuje żadnej kontroli w czasie wykonywania i może prowadzić do nieokreślonego zachowania, gdy wskaźnik faktycznie nie wskazuje pożądanego typu.

struct Base {};
struct Derived : Base {};
Derived d;
Base* p1 = &d;
Derived* p2 = p1;                        // error; cast required
Derived* p3 = static_cast<Derived*>(p1); // OK; p2 now points to Derived object
Base b;
Base* p4 = &b;
Derived* p5 = static_cast<Derived*>(p4); // undefined behaviour since p4 does not
                                         // point to a Derived object

Podobnie odwołanie do klasy bazowej można przekształcić w odwołanie do klasy pochodnej za pomocą static_cast .

struct Base {};
struct Derived : Base {};
Derived d;
Base& r1 = d;
Derived& r2 = r1;                        // error; cast required
Derived& r3 = static_cast<Derived&>(r1); // OK; r3 now refers to Derived object

Jeśli typ źródła jest polimorficzny, można użyć dynamic_cast do wykonania konwersji bazy na pochodną. Wykonuje kontrolę w czasie wykonywania, a awarię można odzyskać zamiast tworzyć niezdefiniowane zachowanie. W przypadku wskaźnika wskaźnik zerowy jest zwracany po awarii. W przypadku odniesienia generowany jest wyjątek w przypadku niepowodzenia typu std::bad_cast (lub klasy pochodnej od std::bad_cast ).

struct Base { virtual ~Base(); }; // Base is polymorphic
struct Derived : Base {};
Base* b1 = new Derived;
Derived* d1 = dynamic_cast<Derived*>(b1); // OK; d1 points to Derived object
Base* b2 = new Base;
Derived* d2 = dynamic_cast<Derived*>(b2); // d2 is a null pointer

Odrzucanie ciągłości

Wskaźnik do obiektu const można przekształcić w wskaźnik do obiektu non-const przy użyciu słowa kluczowego const_cast . W tym przypadku używamy const_cast aby wywołać funkcję, która nie jest const_cast . Akceptuje tylko nietrwały argument char* nawet jeśli nigdy nie pisze przez wskaźnik:

void bad_strlen(char*);
const char* s = "hello, world!";
bad_strlen(s);                    // compile error
bad_strlen(const_cast<char*>(s)); // OK, but it's better to make bad_strlen accept const char*

const_cast na typ referencyjny może być wykorzystany do konwersji wartości const_cast const na wartość niekwalifikowaną do const.

const_cast jest niebezpieczny, ponieważ uniemożliwia systemowi typu C ++ uniemożliwienie próby modyfikacji obiektu const. Takie postępowanie powoduje niezdefiniowane zachowanie.

const int x = 123;
int& mutable_x = const_cast<int&>(x);
mutable_x = 456; // may compile, but produces *undefined behavior*

Wpisz konwersję znakowania

Wskaźnik (odpowiednio odniesienie) do typu obiektu można przekształcić w wskaźnik (odpowiednio odniesienie) do dowolnego innego typu obiektu za pomocą reinterpret_cast . Nie wywołuje to żadnych konstruktorów ani funkcji konwersji.

int x = 42;
char* p = static_cast<char*>(&x);      // error: static_cast cannot perform this conversion
char* p = reinterpret_cast<char*>(&x); // OK
*p = 'z';                              // maybe this modifies x (see below)
C ++ 11

Wynik reinterpret_cast reprezentuje ten sam adres co operand, pod warunkiem, że adres jest odpowiednio wyrównany dla typu miejsca docelowego. W przeciwnym razie wynik nie jest określony.

int x = 42;
char& r = reinterpret_cast<char&>(x);
const void* px = &x;
const void* pr = &r;
assert(px == pr); // should never fire
C ++ 11

Wynik reinterpret_cast jest określony, z wyjątkiem tego, że wskaźnik (lub odniesienie) przetrwa podróż w obie strony od typu źródłowego do typu docelowego iz powrotem, o ile wymóg wyrównania typu docelowego nie jest bardziej rygorystyczny niż typ źródłowy.

int x = 123;
unsigned int& r1 = reinterpret_cast<unsigned int&>(x);
int& r2 = reinterpret_cast<int&>(r1);
r2 = 456; // sets x to 456

W większości implementacji reinterpret_cast nie zmienia adresu, ale wymaganie to nie było znormalizowane do C ++ 11.

reinterpret_cast może być również używany do konwersji z jednego typu wskaźnika na element członkowski danych na inny lub z jednego typu wskaźnika na element członkowski na inny.

Użycie reinterpret_cast jest uważane za niebezpieczne, ponieważ odczyt lub zapis przez wskaźnik lub odniesienie uzyskane za pomocą reinterpret_cast może wyzwalać niezdefiniowane zachowanie, gdy typy źródłowe i docelowe nie są ze sobą powiązane.

Konwersja między wskaźnikiem a liczbą całkowitą

Wskaźnik obiektu (w tym void* ) lub wskaźnik funkcji można przekonwertować na typ całkowity za pomocą reinterpret_cast . Skompiluje się tylko, jeśli typ miejsca docelowego jest wystarczająco długi. Wynik jest zdefiniowany w implementacji i zwykle daje adres numeryczny bajtu w pamięci, na który wskazuje wskaźnik.

Zazwyczaj long lub unsigned long jest wystarczająco długi, aby pomieścić dowolną wartość wskaźnika, ale nie jest to gwarantowane przez standard.

C ++ 11

Jeśli istnieją typy std::intptr_t i std::uintptr_t , na std::uintptr_t są one wystarczająco długie, aby pomieścić void* (a zatem dowolny wskaźnik do typu obiektu). Nie gwarantuje się jednak, że są wystarczająco długie, aby pomieścić wskaźnik funkcji.

Podobnie, reinterpret_cast może być użyty do konwersji typu liczby całkowitej na typ wskaźnika. Ponownie wynik jest zdefiniowany w implementacji, ale gwarantuje, że wartość wskaźnika pozostanie niezmieniona przez podróż w obie strony przez liczbę całkowitą. Norma nie gwarantuje, że wartość zero zostanie przekonwertowana na wskaźnik zerowy.

void register_callback(void (*fp)(void*), void* arg); // probably a C API
void my_callback(void* x) {
    std::cout << "the value is: " << reinterpret_cast<long>(x); // will probably compile
}
long x;
std::cin >> x;
register_callback(my_callback,
                  reinterpret_cast<void*>(x)); // hopefully this doesn't lose information...

Konwersja przez jawnego konstruktora lub jawną funkcję konwersji

Konwersja, która wymaga wywołania jawnego konstruktora lub funkcji konwersji, nie może być wykonana pośrednio. Możemy zażądać jawnej konwersji za pomocą static_cast . Znaczenie jest takie samo jak w przypadku bezpośredniej inicjalizacji, z wyjątkiem tego, że wynik jest tymczasowy.

class C {
    std::unique_ptr<int> p;
  public:
    explicit C(int* p) : p(p) {}
};
void f(C c);
void g(int* p) {
    f(p);                 // error: C::C(int*) is explicit
    f(static_cast<C>(p)); // ok
    f(C(p));              // equivalent to previous line
    C c(p); f(c);         // error: C is not copyable
}

Niejawna konwersja

static_cast może wykonać dowolną niejawną konwersję. Takie użycie static_cast może czasami być przydatne, na przykład w następujących przykładach:

  • Podczas przekazywania argumentów do elipsy „oczekiwany” typ argumentu nie jest statycznie znany, więc nie nastąpi niejawna konwersja.

    const double x = 3.14;
    printf("%d\n", static_cast<int>(x)); // prints 3
    // printf("%d\n", x); // undefined behaviour; printf is expecting an int here
    // alternative:
    // const int y = x; printf("%d\n", y);
    

    Bez jawnej konwersji typu double obiekt zostałby przekazany do elipsy i wystąpiłoby niezdefiniowane zachowanie.

  • Operator przypisania klas pochodnych może wywoływać operatora przypisania klas bazowych w następujący sposób:

    struct Base { /* ... */ };
    struct Derived : Base {
        Derived& operator=(const Derived& other) {
            static_cast<Base&>(*this) = other;
            // alternative:
            // Base& this_base_ref = *this; this_base_ref = other;
        }
    };
    

Przeliczanie enum

static_cast może konwertować z typu liczb całkowitych lub zmiennoprzecinkowych na typ wyliczenia (zarówno o zasięgu, jak i bez zakresu) i odwrotnie. Może także konwertować typy wyliczeń.

  • Konwersja z nieokreślonego typu wyliczenia na typ arytmetyczny jest konwersją niejawną; możliwe jest, ale nie konieczne, użycie static_cast .
C ++ 11
  • Gdy typ wyliczenia o zasięgu zostanie przekonwertowany na typ arytmetyczny:

    • Jeśli wartość wyliczenia może być reprezentowana dokładnie w typie docelowym, wynikiem jest ta wartość.
    • W przeciwnym razie, jeśli typ docelowy jest liczbą całkowitą, wynik nie jest określony.
    • W przeciwnym razie, jeśli typem docelowym jest typ zmiennoprzecinkowy, wynik jest taki sam jak w przypadku konwersji na typ bazowy, a następnie na typ zmiennoprzecinkowy.

    Przykład:

    enum class Format {
        TEXT = 0,
        PDF = 1000,
        OTHER = 2000,
    };
    Format f = Format::PDF;
    int a = f;                         // error
    int b = static_cast<int>(f);       // ok; b is 1000
    char c = static_cast<char>(f);     // unspecified, if 1000 doesn't fit into char
    double d = static_cast<double>(f); // d is 1000.0... probably
    
  • Kiedy typ liczbowy lub typ wyliczenia jest konwertowany na typ wyliczenia:

    • Jeśli oryginalna wartość mieści się w zakresie wyliczenia docelowego, wynikiem jest ta wartość. Zauważ, że ta wartość może być nierówna dla wszystkich modułów wyliczających.
    • W przeciwnym razie wynik jest nieokreślony (<= C ++ 14) lub niezdefiniowany (> = C ++ 17).

    Przykład:

    enum Scale {
        SINGLE = 1,
        DOUBLE = 2,
        QUAD = 4
    };
    Scale s1 = 1;                     // error
    Scale s2 = static_cast<Scale>(2); // s2 is DOUBLE
    Scale s3 = static_cast<Scale>(3); // s3 has value 3, and is not equal to any enumerator
    Scale s9 = static_cast<Scale>(9); // unspecified value in C++14; UB in C++17
    
C ++ 11
  • Kiedy typ zmiennoprzecinkowy jest konwertowany na typ wyliczenia, wynik jest taki sam jak konwersja na typ wyliczeniowy, a następnie na typ wyliczeniowy.

    enum Direction {
        UP = 0,
        LEFT = 1,
        DOWN = 2,
        RIGHT = 3,
    };
    Direction d = static_cast<Direction>(3.14); // d is RIGHT
    

Pochodzi z konwersji podstawowej wskaźników do członków

Wskaźnik do elementu klasy pochodnej można przekonwertować na wskaźnik do elementu klasy podstawowej za pomocą static_cast . Wskazane typy muszą się zgadzać.

Jeśli operand jest pustym wskaźnikiem wartości elementu, wynikiem jest również zerowy wskaźnik wartości elementu.

W przeciwnym razie konwersja jest ważna tylko wtedy, gdy element wskazany przez operand faktycznie istnieje w klasie docelowej lub jeśli klasa docelowa jest klasą bazową lub pochodną klasy zawierającej element wskazany przez operand. static_cast nie sprawdza poprawności. Jeśli konwersja nie jest prawidłowa, zachowanie jest niezdefiniowane.

struct A {};
struct B { int x; };
struct C : A, B { int y; double z; };
int B::*p1 = &B::x;
int C::*p2 = p1;                              // ok; implicit conversion
int B::*p3 = p2;                              // error
int B::*p4 = static_cast<int B::*>(p2);       // ok; p4 is equal to p1
int A::*p5 = static_cast<int A::*>(p2);       // undefined; p2 points to x, which is a member
                                              // of the unrelated class B
double C::*p6 = &C::z;
double A::*p7 = static_cast<double A::*>(p6); // ok, even though A doesn't contain z
int A::*p8 = static_cast<int A::*>(p6);       // error: types don't match

void * do T *

W C ++ void* nie może być niejawnie konwertowany na T* gdzie T jest typem obiektu. Zamiast tego należy użyć static_cast do jawnego wykonania konwersji. Jeśli operand faktycznie wskazuje na obiekt T , wynik wskazuje na ten obiekt. W przeciwnym razie wynik nie jest określony.

C ++ 11

Nawet jeśli operand nie wskazuje obiektu T , o ile operand wskazuje bajt, którego adres jest odpowiednio wyrównany dla typu T , wynik konwersji wskazuje na ten sam bajt.

// allocating an array of 100 ints, the hard way
int* a = malloc(100*sizeof(*a));                    // error; malloc returns void*
int* a = static_cast<int*>(malloc(100*sizeof(*a))); // ok
// int* a = new int[100];                           // no cast needed
// std::vector<int> a(100);                         // better

const char c = '!';
const void* p1 = &c;
const char* p2 = p1;                           // error
const char* p3 = static_cast<const char*>(p1); // ok; p3 points to c
const int* p4 = static_cast<const int*>(p1);   // unspecified in C++03;
                                               // possibly unspecified in C++11 if
                                               // alignof(int) > alignof(char)
char* p5 = static_cast<char*>(p1);             // error: casting away constness

Odlewanie w stylu C.

Rzutowanie w stylu C można uznać za rzutowanie „najlepszego wysiłku” i nazywa się tak, ponieważ jest to jedyne rzutowanie, które można zastosować w C. Składnia tego rzutowania jest (NewType)variable .

Ilekroć ta obsada jest używana, używa jednego z następujących rzutowań c ++ (w kolejności):

  • const_cast<NewType>(variable)
  • static_cast<NewType>(variable)
  • const_cast<NewType>(static_cast<const NewType>(variable))
  • reinterpret_cast<const NewType>(variable)
  • const_cast<NewType>(reinterpret_cast<const NewType>(variable))

Rzutowanie funkcjonalne jest bardzo podobne, choć jako kilka ograniczeń ze względu na jego składnię: NewType(expression) . W rezultacie można rzutować tylko na typy bez spacji.

Lepiej jest użyć nowej obsady c ++, ponieważ jest bardziej czytelna i można ją łatwo zauważyć w dowolnym miejscu kodu źródłowego C ++, a błędy zostaną wykryte w czasie kompilacji, a nie w czasie wykonywania.

Ponieważ ta obsada może spowodować niezamierzony reinterpret_cast , często uważa się ją za niebezpieczną.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow