Kiedy należy stosować odlew statyczny, odlew dynamiczny,odlew const i reinterpret?
Jakie są właściwe zastosowania:
static_cast
dynamic_cast
const_cast
reinterpret_cast
- C-style cast
(type)value
- Obsada w stylu funkcyjnym
type(value)
W Jaki Sposób można zdecydować, które z konkretnych przypadków użyć?
7 answers
static_cast
to pierwsza Obsada, którą powinieneś spróbować użyć. Robi rzeczy takie jak niejawne konwersje między typami (takie jak int
do float
lub wskaźnik do void*
), a także może wywoływać jawne funkcje konwersji (lub niejawne). W wielu przypadkach jawne podanie static_cast
nie jest konieczne, ale ważne jest, aby pamiętać, że składnia T(something)
jest równoważna (T)something
i należy jej unikać (więcej na ten temat później). A T(something, something_else)
jest jednak bezpieczna i gwarantowana, że zadzwoni do konstruktor.
static_cast
może również rzucać poprzez hierarchie dziedziczenia. Nie jest to konieczne podczas rzucania w górę (w kierunku klasy bazowej), ale podczas rzucania w dół może być używane tak długo, jak nie rzuca przez dziedziczenie virtual
. Nie sprawdza jednak i jest niezdefiniowanym zachowaniem static_cast
w dół hierarchii do typu, który w rzeczywistości nie jest typem obiektu.
const_cast
może być użyty do usunięcia lub dodania const
do zmiennej; żaden inny C++ nie jest zdolne do jego usunięcia (nawet reinterpret_cast
). Ważne jest, aby pamiętać, że modyfikowanie dawnej wartości const
jest niezdefiniowane tylko wtedy, gdy oryginalna zmienna to const
; jeśli użyjesz jej do usunięcia const
Z odniesienia do czegoś, co nie zostało zadeklarowane przez const
, jest to bezpieczne. Może to być przydatne np. podczas przeciążania funkcji składowych opartych na const
. Może być również użyty do dodania const
do obiektu, na przykład do wywołania funkcji member overload.
const_cast
działa również podobnie na 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 na dowolny inny typ klasy (Typ polimorficzny ma co najmniej jedną funkcję wirtualną, zadeklarowaną lub dziedziczoną). Możesz go używać nie tylko do rzucania w dół - możesz rzucać na boki lub nawet w górę innego łańcucha. dynamic_cast
wyszukuje żądany obiekt i zwraca go, jeśli to możliwe. Jeśli nie może, powróci nullptr
W w przypadku wskaźnika, lub rzut std::bad_cast
w przypadku odniesienia.
dynamic_cast
ma jednak pewne ograniczenia. To nie działa, jeśli w hierarchii dziedziczenia znajduje się wiele obiektów tego samego typu (tzw. 'straszliwy diament') i nie używasz dziedziczenia virtual
. Może również przejść tylko przez dziedziczenie publiczne - zawsze nie przejdzie przez protected
lub private
dziedziczenie. Rzadko jest to jednak problemem, gdyż takie formy dziedziczenia są Rzadki.
reinterpret_cast
jest najniebezpieczniejszą obsadą i powinna być używana bardzo oszczędnie. Zamienia jeden typ bezpośrednio w inny - na przykład odlewanie wartości z jednego wskaźnika do drugiego lub przechowywanie wskaźnika w int
, lub wszelkiego rodzaju inne paskudne rzeczy. W dużej mierze jedyną gwarancją, jaką otrzymujesz z reinterpret_cast
, jest to, że normalnie, jeśli oddasz wynik z powrotem do oryginalnego typu, otrzymasz dokładnie tę samą wartość (ale Nie jeżeli Typ pośredni jest mniejszy niż oryginalny typ). Istnieje wiele konwersji, które reinterpret_cast
nie da rady. Jest używany przede wszystkim do szczególnie dziwnych konwersji i manipulacji bitami, takich jak przekształcanie nieprzetworzonego strumienia danych w rzeczywiste dane lub przechowywanie danych w niskich bitach wyrównanego wskaźnika.
C-style cast i function-style cast są odlewami za pomocą odpowiednio (type)object
lub type(object)
. Rzut w stylu C jest zdefiniowany jako pierwszy z następujących, który
const_cast
-
static_cast
(choć ignorując ograniczenia dostępu) -
static_cast
(patrz wyżej), następnieconst_cast
reinterpret_cast
-
reinterpret_cast
, następnieconst_cast
Może więc być używany jako zamiennik dla innych odlewów w niektórych przypadkach, ale może być bardzo niebezpieczny ze względu na zdolność przekształcania się w reinterpret_cast
, a ten ostatni powinien być preferowany, gdy potrzebne jest wyraźne odlewanie, chyba że jesteś pewien, że static_cast
powiedzie się lub reinterpret_cast
zawiedzie. Nawet wtedy rozważ dłuższą, bardziej wyraźną opcję.
Odlewy w stylu C ignorują również kontrolę dostępu podczas wykonywania static_cast
, co oznacza, że mają możliwość wykonania operacji, której nie może wykonać żaden inny odlewnik. Jest to jednak w większości kludge, a moim zdaniem jest to kolejny powód, aby unikać rzutów w stylu C.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-16 08:25:06
Użyj dynamic_cast
do konwersji wskaźników / odniesień w hierarchii dziedziczenia.
Użyj static_cast
do konwersji zwykłych typów.
Użyj reinterpret_cast
do niskopoziomowej reinterpretacji wzorców bitowych. Stosować ze szczególną ostrożnością.
Użyj const_cast
do odrzucenia const/volatile
. Unikaj tego, chyba że utkniesz przy użyciu niepoprawnego API.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-04 07:48:17
(Wiele teoretycznych i pojęciowych wyjaśnień zostało podanych powyżej)
Poniżej kilka z praktycznych przykładów kiedy użyłem static_cast, dynamic_cast, const_cast, reinterpret_cast .
(odnosi się również do tego, 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-11 00:22:00
To może pomóc, jeśli znasz trochę wewnętrznych...
Static_cast
- kompilator C++ wie już, jak przekonwertować typy skalerów, takie jak float do int. Użyj
static_cast
dla nich. - w ogólnym przypadku konwersji z typu
A
naB
,static_cast
wywołanie konstruktoraB
przekazującegoA
jako param. JeśliB
nie ma takiego konstruktora, to pojawia się błąd czasu kompilacji. - rzut od
A*
doB*
zawsze się powiedzie, jeśli a i B są w dziedziczeniu hierarchia (lub void) w przeciwnym razie dostajesz błąd kompilacji. -
Gotcha : jeśli rzucisz wskaźnik bazowy na wskaźnik Pochodny, ale jeśli rzeczywisty obiekt nie jest tak naprawdę typem pochodnym, to nie otrzymasz błędu. Masz zły wskaźnik i segfault w czasie wykonywania. To samo dotyczy
A&
doB&
. - Gotcha : Cast from Derived to Base or viceversa creates new copy! Dla ludzi wywodzących się z C# / Javy może to być ogromny niespodzianka.
Dynamic_cast
- dynamic_cast używa informacji typu runtime, aby dowiedzieć się, czy cast jest poprawny. Na przykład,
(Base*)
do(Derived*)
może się nie udać, jeśli wskaźnik nie jest typem pochodnym. - oznacza to, że dynamic_cast jest bardzo drogi w porównaniu do static_cast!
- dla
A*
doB*
, jeśli Cast jest nieprawidłowy, to dynamic_cast zwróci nullptr. - For
A&
toB&
if cast is invalid then dynamic_cast will throw bad_cast wyjątek. - W przeciwieństwie do innych odlewów, istnieje nadmiarowość runtime.
Const_cast
- podczas gdy static_cast może wykonywać non-const do const, nie może iść w drugą stronę. Const_cast może działać w obie strony.
- jednym z przykładów, gdzie jest to przydatne, jest iteracja przez jakiś kontener podobny do
set<T>
, który zwraca tylko swoje elementy jako const, aby upewnić się, że nie zmienisz jego klucza. Jeśli jednak chcesz zmodyfikować inne elementy obiektu, to powinno być ok. Możesz użyć const_cast, aby usunąć constness. - Innym przykładem jest, gdy chcesz zaimplementować
T& foo()
, a takżeconst T& foo()
. Aby uniknąć powielania kodu, możesz zastosować const_cast do zwracania wartości jednej funkcji z drugiej.
Reinterpret_cast
- to w zasadzie mówi, że pobieramy te bajty w tym miejscu pamięci i traktujemy je jako dany obiekt.
- na przykład, można załadować 4 bajty float do 4 bajtów int, aby zobaczyć jak bity w float wyglądają na przykład.
- oczywiście, jeśli dane nie są poprawne dla typu, możesz uzyskać segfault.
- nie ma nadmiarowego czasu wykonania dla tej obsady.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 04:56:49
Czyto odpowiada na twoje pytanie?
Nigdy nie używałem reinterpret_cast
i zastanawiam się, czy wpadanie w sprawę, która tego potrzebuje, nie jest zapachem złego designu. W kodzie bazowym, na którym pracuję dynamic_cast
jest często używany. Różnica w stosunku do static_cast
polega na tym, że dynamic_cast
wykonuje sprawdzanie, które może (bezpieczniejsze) lub nie (więcej kosztów) być tym, czego chcesz (zobacz msdn).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-04 16:31:31
Oprócz innych odpowiedzi do tej pory, tutaj jest nieoczywisty przykład, gdzie {[2] } nie wystarcza, aby reinterpret_cast
było 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 klasy bazowej). Prawdziwym przykładem takiej funkcji jest CoCreateInstance()
(Zobacz ostatni parametr, który w rzeczywistości jest void**
). Załóżmy, że żądasz określonej klasy obiektu z tej funkcji, więc znasz z góry typ wskaźnika (co często robisz dla obiektów COM). W tym przypadku nie możesz dodać wskaźnika do wskaźnika do 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 dla prostych wskaźników (nie dla wskaźników do wskaźników), więc powyższy kod można przepisać, aby uniknąć reinterpret_cast
(za cenę 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);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-22 10:30:03
Podczas gdy inne odpowiedzi ładnie opisywały wszystkie różnice między odlewami C++, chciałbym dodać krótką notatkę, dlaczego nie powinieneś używać odlewów w stylu C (Type) var
i Type(var)
.
Dla początkujących C++ odlewy w stylu C wyglądają jak operacja superset nad odlewami w C++ (static_cast (), dynamic_cast (), const_cast (), reinterpret_cast ()) i ktoś mógłby preferować je nad odlewami w C++. W rzeczywistości Obsada w stylu C jest superset i krótsza do napisania.
Głównym problemem odlewów w stylu C jest to, że ukrywają prawdziwe intencje obsady. Odlewy w stylu C mogą wykonywać praktycznie wszystkie typy odlewów z normalnie bezpiecznych odlewów wykonanych przez static_cast () i dynamic_cast () do potencjalnie niebezpiecznych odlewów, takich jak const (), gdzie modyfikator const może być usunięty, więc zmienne const mogą być modyfikowane i reinterpret_cast (), które mogą nawet reinterpretować wartości całkowite do wskaźników.
Oto próbka.int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
Głównym powodem dodania C++ do języka było umożliwienie programista, aby wyjaśnić swoje intencje-dlaczego zamierza to zrobić. Używając odlewów w stylu C, które są doskonale poprawne w C++, sprawiasz, że Twój kod jest mniej czytelny i bardziej podatny na błędy, szczególnie dla innych programistów, którzy nie stworzyli Twojego kodu. Tak więc, aby Twój kod był bardziej czytelny i wyraźny, zawsze powinieneś preferować odlewy w C++ niż odlewy w stylu C.
Oto krótki cytat z książki Bjarne Stroustrupa (autora C++) The C++ Programming Language 4th edition-strona 302.
Ta Obsada w stylu C jest znacznie bardziej niebezpieczna niż nazwane operatory konwersji ponieważ zapis jest trudniejszy do zauważenia w dużym programie, a rodzaj konwersji zamierzony przez programistę nie jest jednoznaczny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-22 11:18:21