Pytanie Kiedy należy użyć static_cast, dynamic_cast, const_cast i reinterpret_cast?


Jakie są właściwe zastosowania:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • Odlew w stylu C (type)value
  • Odlewanie w stylu funkcyjnym type(value)

W jaki sposób można zdecydować, którego użyć w jakich konkretnych przypadkach?


2052
2017-12-01 20:11


pochodzenie


Może dobre referencje tutaj:Jak wyjaśnić różnice między static_cast, reinterpret_cast, const_cast i dynamic_cast na nowego programistę C ++?. - Nan Xiao
W przypadku niektórych użytecznych konkretnych przykładów używania różnych rodzajów rzutów, możesz sprawdzić pierwszą odpowiedź na podobne pytanie w ten inny temat. - TeaMonkie


Odpowiedzi:


static_cast jest pierwszą obsadą, którą powinieneś spróbować użyć. Robi takie rzeczy jak niejawne konwersje między typami (np int do floatlub wskaźnik do void*), a także może wywoływać jawne funkcje konwersji (lub niejawne). W wielu przypadkach jednoznacznie stwierdzające static_cast nie jest konieczne, ale ważne jest, aby pamiętać, że T(something) składnia jest równoważna (T)something i należy ich unikać (więcej o tym później). ZA T(something, something_else) jest jednak bezpieczny i gwarantuje wywołanie konstruktora.

static_cast może również rzucać hierarchie dziedziczenia. Nie jest to konieczne podczas rzucania w górę (w kierunku klasy bazowej), ale podczas rzucania w dół może być używany tak długo, jak długo nie jest rzucany virtual dziedzictwo. Nie sprawdza jednak, czy jest to niezdefiniowane zachowanie static_cast w dół hierarchii do typu, który nie jest faktycznie typem obiektu.


const_cast można użyć do usunięcia lub dodania const do zmiennej; żadna inna obsada C ++ nie jest w stanie go usunąć (nawet nie reinterpret_cast). Ważne jest, aby pamiętać, że wcześniejsza modyfikacja const wartość jest tylko niezdefiniowana, jeśli oryginalna zmienna jest const; jeśli używasz go, aby wziąć const od odwołania do czegoś, co nie zostało zadeklarowane const, to jest bezpieczne. Może to być przydatne w przypadku przeciążania funkcji składowych na podstawie const, na przykład. Można go również użyć do dodania const do obiektu, na przykład do wywołania przeciążenia funkcji składowych.

const_cast działa również podobnie volatile, choć to mniej powszechne.


dynamic_cast jest prawie wyłącznie używany do obsługi polimorfizmu. Możesz rzucić wskaźnik lub odniesienie do dowolnego typu polimorficznego do dowolnego innego typu klasy (typ polimorficzny ma co najmniej jedną funkcję wirtualną, zadeklarowaną lub odziedziczoną). Możesz go użyć do czegoś więcej niż rzucania w dół - możesz rzucić na boki lub nawet w inny łańcuch. The dynamic_cast będzie szukał pożądanego obiektu i zwróci go, jeśli to możliwe. Jeśli nie, wróci nullptr w przypadku wskaźnika lub rzutu std::bad_cast w przypadku odniesienia.

dynamic_cast ma jednak pewne ograniczenia. Nie działa, jeśli istnieje wiele obiektów tego samego typu w hierarchii dziedziczenia (tak zwany "przerażający diament"), a ty nie używasz virtual dziedzictwo. Może również przejść tylko przez dziedziczenie publiczne - zawsze nie uda się do niego dotrzeć protected lub private dziedzictwo. Jednak rzadko jest to problem, ponieważ takie formy dziedziczenia są rzadkością.


reinterpret_cast jest najbardziej niebezpieczną obsadą i powinno być używane bardzo oszczędnie. Zamienia jeden typ bezpośrednio w drugi - na przykład rzutowanie wartości z jednego wskaźnika na drugi lub zapisywanie wskaźnika w intlub wszelkiego rodzaju inne nieprzyjemne rzeczy. W dużej mierze jedyna gwarancja, którą możesz uzyskać reinterpret_cast jest to, że normalnie, jeśli rzutujesz wynik z powrotem na pierwotny typ, otrzymasz dokładnie taką samą wartość (ale nie jeśli typ pośredni jest mniejszy niż typ pierwotny). Istnieje kilka konwersji, które reinterpret_cast też nie mogę. Jest używany głównie do wyjątkowo dziwnych konwersji i manipulacji bitami, takich jak przekształcenie nieprzetworzonego strumienia danych w rzeczywiste dane lub przechowywanie danych w najniższych bitach wyrównanego wskaźnika.


Odlew w stylu C i obsada w stylu funkcyjnym są odlewane za pomocą (type)object lub type(object), odpowiednio. Rzucanie w stylu C jest zdefiniowane jako pierwsze z następujących:

  • const_cast
  • static_cast (choć ignoruje ograniczenia dostępu)
  • static_cast (patrz wyżej) const_cast
  • reinterpret_cast
  • reinterpret_cast, następnie const_cast

W niektórych przypadkach może być stosowany jako zamiennik innych rzutów, ale może być bardzo niebezpieczny ze względu na możliwość przeniesienia do reinterpret_cast, a ta druga powinna być preferowana, gdy wymagany jest wyraźny rzut, chyba że jesteś tego pewien static_cast odniesie sukces reinterpret_cast zawiedzie. Nawet wtedy rozważ dłuższą, bardziej wyraźną opcję.

Odlewy w stylu C również ignorują kontrolę dostępu podczas wykonywania static_cast, co oznacza, że ​​mają możliwość wykonania operacji, której nie może wykonać żadna inna obsada. Jest to w większości kałuża, a moim zdaniem jest to kolejny powód, by unikać rzutów w stylu C.


2211
2017-12-01 20:22



dynamic_cast jest tylko dla typów polimorficznych. musisz go używać tylko podczas przesyłania do klasy pochodnej. static_cast jest z pewnością pierwszą opcją, chyba że potrzebujesz funkcji dynamic_cast. To nie jest jakaś cudowna srebrna kula "typowo sprawdzająca obsada" w ogóle. - jalf
Świetna odpowiedź! Jedna szybka uwaga: static_cast może być konieczne, aby rzucić hierarchię w przypadku, gdy masz wyprowadzony * i wrzucić do Base * &, ponieważ podwójne wskaźniki / referencje nie automagicznie rzucają hierarchię. Natrafiłem na taką (szczerze mówiąc, nieczęstą) sytuację dwie minuty temu. ;-) - bartgol
* "żadna inna obsada C ++ nie jest w stanie usunąć const (nawet nie reinterpret_cast) "... naprawdę?" A co z reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))? - Mehrdad
Myślę, że ważnym szczegółem brakuje wyżej jest to, że dynamic_cast ma wydajność w czasie wykonania kary w porównaniu do statycznego lub reinterpret_cast. Jest to ważne, np. w oprogramowaniu czasu rzeczywistego. - jfritz42
Warto o tym wspomnieć reinterpret_cast jest często bronią z wyboru w przypadku zestawu nieprzejrzystych typów danych API - camelCase


Posługiwać się dynamic_cast do konwersji wskaźników / referencji w hierarchii dziedziczenia.

Posługiwać się static_cast dla zwykłych konwersji typu.

Posługiwać się reinterpret_cast do reinterpretacji niskich poziomów wzorów bitowych. Używaj z niezwykłą ostrożnością.

Posługiwać się const_cast do odrzucania const/volatile. Unikaj tego, chyba że utkniesz przy użyciu const-incorrect API.


284
2018-01-21 04:53





(Wiele wyjaśnień teoretycznych i koncepcyjnych podano powyżej) 

Poniżej znajdują się niektóre z praktyczne przykłady kiedy użyłem static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Również to sugeruje, aby zrozumieć wyjaśnienie: http://www.cplusplus.com/doc/tutorial/typecasting/)

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

152
2017-12-11 02:05



Teoria niektórych innych odpowiedzi jest dobra, ale nadal myląca, widząc te przykłady po przeczytaniu innych odpowiedzi, naprawdę wszystko ma sens. To bez przykładów, nadal nie byłem pewien, ale z nimi jestem teraz pewien, co oznaczają inne odpowiedzi. - Solx
O ostatnim użyciu reinterpret_cast: nie jest to to samo co użycie static_cast<char*>(&val) ? - Lorenzo Belli
@LorenzoBelli Oczywiście, że nie. Spróbowałeś? To ostatnie nie jest poprawne w C ++ i blokuje kompilację. static_cast działa tylko między typami z określonymi konwersjami, widoczną relacją dziedziczenia lub do / z void *. Do wszystkiego innego są inne odlewy. reinterpret cast do każdego char * typ jest dozwolony, aby umożliwić odczytanie reprezentacji dowolnego obiektu - i jeden z niewielu przypadków, w których to słowo kluczowe jest użyteczne, a nie nieokiełznany generator zachowań implementacyjnych / nieokreślonych. Ale nie jest to uważane za "normalne" nawrócenie, więc nie jest dozwolone (zazwyczaj) bardzo konserwatywne static_cast. - underscore_d
reinterpret_cast jest dość powszechne, gdy pracujesz z oprogramowaniem systemowym, takim jak bazy danych. W większości przypadków piszesz własnego menedżera stron, który nie ma pojęcia o tym, jaki typ danych jest przechowywany na stronie i po prostu zwraca wskaźnik nieważności. To na wyższych poziomach, aby ponownie zinterpretować obsadę i wywnioskować, jak chcą. - Sohaib


To może pomóc, jeśli wiesz trochę wewnętrznych elementów ...

static_cast

  • Kompilator C ++ już wie, jak konwertować typy skalerów, takie jak float na int. Użyj static_cast dla nich.
  • Ogólnie rzecz biorąc, konwertując typ A na B, static_cast wywołuje konstruktor B przekazując go A. Jeśli B nie ma takiego konstruktora, wówczas otrzymasz błąd kompilacji.
  • Przesyłaj z A* do B* zawsze kończy się sukcesem, jeśli A i B są w hierarchii dziedziczenia (lub nieważne) w przeciwnym razie wystąpi błąd kompilacji.
  • Mam cię: Jeśli rzucisz wskaźnik bazowy na wskaźnik pochodny, ale jeśli rzeczywisty obiekt jest, jeśli nie jest typem pochodnym, to ty nie rób tego dostaję błąd. Otrzymujesz zły wskaźnik i gdy tylko spróbujesz uzyskać dostęp do członków pochodnego wskaźnika, otrzymasz błąd segfault w czasie wykonywania.
  • To samo dotyczy A& do B&.
  • Mam cię: Przesyłaj od Derived do Base lub viceversa tworzy nową kopię! Dla osób pochodzących z C # / Java wiele z powyższych może być ogromną niespodzianką.

dynamic_cast

  • dynamic_cast używa informacji o typie działania, aby dowiedzieć się, czy rzutowanie jest prawidłowe. Na przykład, (Base*) do (Derived*)może się nie udać, jeśli wskaźnik nie jest faktycznie typu pochodnego.
  • Oznacza to, że dynamic_cast jest bardzo kosztowny w porównaniu do static_cast!
  • Dla A* do B*, jeśli rzutowanie jest nieważne, to dynamic_cast zwróci nullptr.
  • Dla A& do B& jeśli rzutowanie jest nieważne, dynamic_cast rzuci wyjątek bad_cast.
  • W przeciwieństwie do innych rzutów, nie ma kosztów ogólnych.

const_cast

  • Podczas gdy static_cast może zrobić const do const, nie może obejść się inaczej. Funkcja const_cast może działać w obie strony.
  • Jednym z przykładów, w którym to się przydaje, jest iterowanie przez jakiś kontener set<T> która zwraca tylko jego elementy jako const, aby upewnić się, że nie zmienisz swojego klucza. Jednak jeśli twoim zamiarem jest modyfikowanie nie-kluczowych elementów obiektu, powinno być dobrze. Możesz użyć const_cast, aby usunąć constness.
  • Innym przykładem jest moment, w którym chcesz zaimplementować T& foo() jak również const T& foo(). Aby uniknąć powielania kodu, możesz zastosować funkcję const_cast, aby zwrócić wartość jednej funkcji z drugiej.

reinterpret_cast

  • To w zasadzie mówi, że weź te bajty w tej lokalizacji pamięci i pomyśl o tym jako podanym obiekcie.
  • Na przykład, możesz załadować 4 bajty float do 4 bajtów int, aby zobaczyć, jak wyglądają bity w float.
  • Oczywiście, jeśli dane nie są poprawne dla danego typu, możesz uzyskać błąd segfault.
  • W przypadku tej obsady nie ma nakładu czasu wykonywania.

52
2017-12-01 20:20



Ostatni punkt o const_cast jest niepoprawny: tylko dynamic_cast w rzeczywistości ma obciążenie runtime. Wygląda na to, że możesz zmienić kolejność i zapomniałeś przenieść i zmienić to zdanie. - rubenvb
Twoje są poprawne! Naprawiony. - ShitalShah


Robi to odpowiedzieć na twoje pytanie?

Nigdy nie użyłem reinterpret_casti zastanawiam się, czy w przypadku, który tego wymaga, nie jest to zapach złego projektu. W bazie kodu pracuję dynamic_cast jest często używany. Różnica z static_cast Czy to jest dynamic_cast sprawdza środowisko wykonawcze, które może (bezpieczniejsze) lub nie (więcej narzutów) być tym, czego potrzebujesz (zob msdn).


11
2018-05-31 14:16



Użyłem reintrepret_cast w jednym celu - wydobycie bitów z podwójnego (ten sam rozmiar co na długo na mojej platformie). - Joshua
reinterpret_cast jest potrzebna np. do pracy z obiektami COM. CoCreateInstance () ma parametr wyjściowy typu void ** (ostatni parametr), w którym przekazujesz wskaźnik zadeklarowany jako np. "INetFwPolicy2 * pNetFwPolicy2". Aby to zrobić, musisz napisać coś w stylu reinterpret_cast <void **> (& pNetFwPolicy2). - Serge Rogatch


Oprócz innych odpowiedzi do tej pory, tutaj jest nieoczywisty przykład, gdzie static_cast nie jest wystarczający, aby reinterpret_cast jest potrzebne. Załóżmy, że istnieje funkcja, która w parametrze wyjściowym zwraca wskaźniki do obiektów różnych klas (które nie mają wspólnej wspólnej klasy bazowej). Prawdziwym przykładem takiej funkcji jest CoCreateInstance() (zobacz ostatni parametr, który jest w rzeczywistości void**). Załóżmy, że żądasz określonej klasy obiektu od tej funkcji, więc znasz wcześniej typ wskaźnika (który często robisz dla obiektów COM). W takim przypadku nie można rzucić wskaźnika do wskaźnika void** z static_cast: potrzebujesz reinterpret_cast<void**>(&yourPointer).

W kodzie:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

Jednak, static_cast działa na proste wskaźniki (nie na wskaźniki do wskaźników), więc powyższy kod można przepisać, aby uniknąć reinterpret_cast (po cenie dodatkowej zmiennej) w następujący sposób:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

9