Nie zgadzam się, to drugie pytanie dotyczyło opisu różnic między obsadami wprowadzonymi w C ++. To pytanie dotyczy rzeczywistej użyteczności static_cast, która jest nieco inna.
Vincent Robert,
2
Z pewnością moglibyśmy połączyć dwa pytania, ale tym, co musielibyśmy zachować w tym wątku, jest przewaga używania funkcji nad rzutowaniem w stylu C, o czym obecnie wspomina się tylko w odpowiedzi w jednym wierszu w drugim wątku, bez głosów .
Tommy Herbert
6
To pytanie dotyczy typów „wbudowanych”, takich jak int, natomiast pytanie dotyczy typów klas. Wydaje się, że jest to wystarczająco znacząca różnica, aby zasługiwać na osobne wyjaśnienie.
9
static_cast to tak naprawdę operator, a nie funkcja.
ThomasMcLeod
Odpowiedzi:
633
Głównym powodem jest to, że klasyczne odlewy C nie dokonują rozróżnienia między tym, co nazywamy static_cast<>(), reinterpret_cast<>(), const_cast<>(), i dynamic_cast<>(). Te cztery rzeczy są zupełnie inne.
A static_cast<>()jest zwykle bezpieczny. Istnieje poprawna konwersja w języku lub odpowiedni konstruktor, który to umożliwia. Jedynie raz jest to trochę ryzykowne, kiedy rzucisz się na dziedziczną klasę; musisz upewnić się, że obiekt jest w rzeczywistości potomkiem, o którym się twierdzi, że jest zewnętrzny w stosunku do języka (jak flaga w obiekcie). A dynamic_cast<>()jest bezpieczne, dopóki wynik jest sprawdzany (wskaźnik) lub możliwy wyjątek jest uwzględniany (odniesienie).
Z drugiej strony A reinterpret_cast<>()(lub a const_cast<>()) jest zawsze niebezpieczne. Mówisz kompilatorowi: „zaufaj mi: wiem, że to nie wygląda foo(wygląda na to, że nie można go modyfikować), ale tak jest”.
Pierwszym problemem jest to, że prawie niemożliwe jest określenie, który wystąpi w obsadzie w stylu C, bez patrzenia na duże i rozproszone fragmenty kodu i znajomości wszystkich reguł.
CDerivedClass*pMyObject;
pMyObject =static_cast<CDerivedClass*>(pSomething);// Safe; as long as we checked
pMyObject =(CDerivedClass*)(pSomething);// Same as static_cast<>// Safe; as long as we checked// but harder to read
Zobaczmy jednak prawie identyczny kod:
CMyOtherStuff*pOther;
pOther =static_cast<CMyOtherStuff*>(pSomething);// Compiler error: Can't convert
pOther =(CMyOtherStuff*)(pSomething);// No compiler error.// Same as reinterpret_cast<>// and it's wrong!!!
Jak widać, nie ma łatwego sposobu na rozróżnienie tych dwóch sytuacji bez wiedzy o wszystkich zaangażowanych klasach.
Drugi problem polega na tym, że rzutowanie w stylu C jest zbyt trudne do zlokalizowania. W złożonych wyrażeniach może być bardzo trudno zobaczyć rzutki w stylu C. Jest praktycznie niemożliwe napisanie zautomatyzowanego narzędzia, które musi zlokalizować rzutowania w stylu C (na przykład narzędzie wyszukiwania) bez pełnego interfejsu kompilatora C ++. Z drugiej strony łatwo jest wyszukać „static_cast <” lub „reinterpret_cast <”.
pOther =reinterpret_cast<CMyOtherStuff*>(pSomething);// No compiler error.// but the presence of a reinterpret_cast<> is // like a Siren with Red Flashing Lights in your code.// The mere typing of it should cause you to feel VERY uncomfortable.
Oznacza to, że nie tylko rzuty w stylu C są bardziej niebezpieczne, ale znacznie trudniej jest znaleźć je wszystkie, aby upewnić się, że są prawidłowe.
Nie należy używać static_castdo odrzucania hierarchii dziedziczenia, ale raczej dynamic_cast. Zwróci to wskaźnik zerowy lub prawidłowy wskaźnik.
David Thornley,
41
@David Thornley: Zwykle się zgadzam. Myślę, że zasygnalizowałem ostrzeżenia, które należy zastosować static_castw tej sytuacji. dynamic_castmoże być bezpieczniejszy, ale nie zawsze jest to najlepsza opcja. Czasami wiesz, że wskaźnik wskazuje na podtyp, nieprzezroczysty dla kompilatora, a a static_castjest szybszy. W co najmniej niektórych środowiskach dynamic_castwymaga opcjonalnej obsługi kompilatora i kosztu działania (włączenie RTTI) i możesz nie chcieć włączać go tylko dla kilku kontroli, które możesz wykonać samodzielnie. RTTI C ++ jest tylko jednym możliwym rozwiązaniem problemu.
Euro Micelli,
18
Twoje twierdzenie o rzutach C jest fałszywe. Wszystkie rzutowania C są konwersjami wartości, z grubsza porównywalnymi do C ++ static_cast. Odpowiednikiem C reinterpret_castjest *(destination_type *)&, tzn. Pobranie adresu obiektu, rzutowanie tego adresu na wskaźnik na inny typ, a następnie dereferencje. Z wyjątkiem przypadku typów znaków lub niektórych typów struktur, dla których C definiuje zachowanie tego konstruktu, generalnie powoduje to niezdefiniowane zachowanie w C.
R .. GitHub STOP HELPING ICE
11
Twoja dokładna odpowiedź dotyczy treści postu. Szukałem odpowiedzi na tytuł „po co używać static_cast <int> (x) zamiast (int) x”. To jest, dla typu int(i inttylko), dlaczego użycie static_cast<int>vs. (int)jako jedyna korzyść wydaje się być ze zmiennymi klasowymi i wskaźnikami. Poproś o rozwinięcie tego.
chux - Przywróć Monikę
31
@chux, for intdynamic_castnie ma zastosowania, ale wszystkie pozostałe powody są ważne . Na przykład: powiedzmy, że vjest parametrem funkcji zadeklarowanym jako float, to (int)vjest static_cast<int>(v). Ale jeśli zmienisz parametr na float*, (int)vpo cichu staje się reinterpret_cast<int>(v)chwilowo static_cast<int>(v)nielegalne i poprawnie przechwytywane przez kompilator.
Euro Micelli,
115
Jedna pragmatyczna wskazówka: możesz łatwo wyszukać słowo kluczowe static_cast w kodzie źródłowym, jeśli planujesz uporządkować projekt.
możesz wyszukiwać za pomocą nawiasów, np. „(int)”, ale dobra odpowiedź i uzasadniony powód do używania rzutowania w stylu C ++.
Mike
4
@Mike, który znajdzie fałszywe alarmy - deklaracja funkcji z jednym intparametrem.
Nathan Osman
1
Może to dawać fałszywe negatywy: jeśli przeszukujesz bazę kodu, w której nie jesteś jedynym autorem, nie znajdziesz rzutów w stylu C, które inni mogliby wprowadzić z pewnych powodów.
Ruslan
7
Jak zrobienie tego pomogłoby uporządkować projekt?
Bilow
Nie szukałbyś static_cast, ponieważ najprawdopodobniej jest on poprawny. Chcesz odfiltrować static_cast, podczas wyszukiwania reinterpret_cast, const_cast, a może nawet dynamic_cast, ponieważ wskazują one miejsca, które można przeprojektować. C-cast miesza się razem i nie daje ci powodu do rzucania.
Dragan
78
W skrócie :
static_cast<>() daje ci możliwość sprawdzania czasu kompilacji, nie ma obsady w stylu C.
static_cast<>()może być łatwo zauważony w dowolnym miejscu w kodzie źródłowym C ++; przeciwnie, obsada C_Style jest trudniejsza do wykrycia.
Intencje są przekazywane znacznie lepiej przy użyciu rzutowań C ++.
Więcej wyjaśnień :
Obsada statyczna wykonuje konwersje między zgodnymi typami . Jest podobny do obsady w stylu C, ale jest bardziej restrykcyjny. Na przykład rzutowanie w stylu C umożliwia wskaźnikowi całkowitemu wskazywanie znaku.
char c =10;// 1 byteint*p =(int*)&c;// 4 bytes
Ponieważ skutkuje to 4-bajtowym wskaźnikiem wskazującym na 1 bajt przydzielonej pamięci, zapis do tego wskaźnika spowoduje błąd w czasie wykonywania lub nadpisze przylegającą pamięć.
*p =5;// run-time error: stack corruption
W przeciwieństwie do rzutowania w stylu C, rzutowanie statyczne pozwoli kompilatorowi sprawdzić, czy typy danych wskaźnika i pointee są kompatybilne, co pozwala programiście przechwycić to nieprawidłowe przypisanie wskaźnika podczas kompilacji.
Nie zgadzam się, że static_cast<>()jest to bardziej czytelne. Mam na myśli, że czasami tak jest, ale przez większość czasu - szczególnie na podstawowych typach liczb całkowitych - jest po prostu okropnie i niepotrzebnie gadatliwy. Na przykład: Jest to funkcja, która zamienia bajty 32-bitowego słowa. static_cast<uint##>()Czytanie przy użyciu rzutów byłoby prawie niemożliwe , ale dość łatwe do zrozumienia przy użyciu (uint##)rzutów. Zdjęcie kodu: imgur.com/NoHbGve
Todd Lehman
3
@ToddLehman: Dziękuję, ale ja też nie powiedziałem always. (ale w większości przypadków tak) Są pewne przypadki, w których rzutowanie w stylu c jest znacznie bardziej czytelne. To jeden z powodów, dla których casting w stylu c jest wciąż aktywny i działa w imho c ++. :) Nawiasem mówiąc, był to bardzo ładny przykład
Rika
8
Kod @ToddLehman na tym obrazie używa dwóch rzutowań łańcuchowych ( (uint32_t)(uint8_t)), aby osiągnąć, że bajty oprócz najniższych są resetowane. Do tego jest bitowe i ( 0xFF &). Użycie obsad zaciemnia intencję.
Öö Tiib,
28
Pytanie jest większe niż tylko użycie rzutowania static_cast lub rzutowania w stylu C, ponieważ podczas korzystania z rzutów w stylu C zachodzą różne rzeczy. Operatory rzutowania w C ++ mają na celu uwydatnienie tych operacji.
Na powierzchni rzutowania w stylu static_cast i C wyglądają tak samo, na przykład podczas rzutowania jednej wartości na drugą:
int i;double d =(double)i;//C-style castdouble d2 =static_cast<double>( i );//C++ cast
Oba z nich podają wartość całkowitą na podwójną. Jednak podczas pracy ze wskaźnikami sprawy stają się bardziej skomplikowane. kilka przykładów:
class A {};class B :public A {};
A* a =new B;
B* b =(B*)a;//(1) what is this supposed to do?char* c =(char*)newint(5);//(2) that weird?char* c1 =static_cast<char*>(newint(5));//(3) compile time error
W tym przykładzie (1) być może OK, ponieważ obiekt wskazywany przez A jest tak naprawdę instancją B. Ale co, jeśli nie wiesz w tym momencie kodu, na co właściwie wskazuje? (2) może całkowicie legalny (chcesz spojrzeć tylko na jeden bajt liczby całkowitej), ale może to być również błąd, w którym to przypadku błąd byłby miły, jak (3). Operatory rzutowania w C ++ mają na celu ujawnienie tych problemów w kodzie, zapewniając błędy kompilacji lub wykonania w miarę możliwości.
Zatem do ścisłego „rzutowania wartości” możesz użyć static_cast. Jeśli chcesz odlewania polimorficznego wskaźników w czasie wykonywania, użyj dynamic_cast. Jeśli naprawdę chcesz zapomnieć o typach, możesz użyć reintrepret_cast. Aby po prostu wyrzucić const przez okno, jest const_cast.
Po prostu uwydatniają kod, dzięki czemu wygląda na to, że wiesz, co robiłeś.
Dodatkowe (choć raczej niewielkie) zalety w porównaniu z obsadą w stylu C to to, że wyróżnia się bardziej (robienie czegoś potencjalnie złego powinno wyglądać brzydko) i jest bardziej grep.
Michael Burr,
4
W mojej książce zdolność grep jest zawsze plusem.
Branan
7
Pozwala łatwo znaleźć rzutowania w kodzie za pomocą grep lub podobnych narzędzi.
Wyjaśnia, jaki rodzaj obsady wykonujesz, i angażujesz pomoc kompilatora w egzekwowaniu tego. Jeśli chcesz tylko odrzucić wiązanie, możesz użyć const_cast, co nie pozwoli ci wykonywać innych rodzajów konwersji.
Rzutowania są z natury brzydkie - Ty jako programista unieważniasz, jak kompilator normalnie traktowałby twój kod. Mówisz do kompilatora: „Wiem lepiej od ciebie”. W związku z tym sensowne jest, aby wykonanie obsady było umiarkowanie bolesne i że powinno wystarczyć w kodzie, ponieważ są prawdopodobnym źródłem problemów.
Całkowicie się z tym zgadzam w przypadku klas, ale czy używanie rzutów w stylu C ++ dla typów POD ma jakiś sens?
Zachary Kraus
Chyba tak. Wszystkie 3 powody dotyczą POD i warto mieć tylko jedną regułę zamiast osobnych dla klas i POD.
JohnMcG
Interesujące, być może będę musiał zmodyfikować sposób, w jaki wykonuję rzutowania w przyszłym kodzie dla typów POD.
Zachary Kraus
7
Chodzi o to, ile bezpieczeństwa typu chcesz narzucić.
Kiedy piszesz (bar) foo(co jest równoważne zreinterpret_cast<bar> foo jeśli nie podałeś operatora konwersji typu), mówisz kompilatorowi, aby zignorował bezpieczeństwo typu, i po prostu rób to, co mu kazano.
Kiedy piszesz static_cast<bar> foo, pytasz kompilatora, aby przynajmniej sprawdził, czy konwersja typu ma sens, a dla typów integralnych wstawił kod konwersji.
EDYCJA 26.02.2014
Napisałem tę odpowiedź ponad 5 lat temu i pomyliłem się. (Patrz komentarze.) Ale wciąż zyskuje poparcie!
(bar) foo nie jest równoważne z reinterpretacją_cast <bar> (foo). Reguły dla „(TYPE) expr” są takie, że wybierze odpowiednią obsadę w stylu C ++, która może obejmować reinterpret_cast.
Richard Corden,
Słuszna uwaga. Euro Micelli udzieliło ostatecznej odpowiedzi na to pytanie.
Pitarou,
1
Poza tym static_cast<bar>(foo)w nawiasach. To samo dotyczy reinterpret_cast<bar>(foo).
LF,
6
Rzutów w stylu C można łatwo przeoczyć w bloku kodu. Rzutowania w stylu C ++ są nie tylko lepszą praktyką; oferują znacznie większą elastyczność.
reinterpret_cast pozwala na konwersję typu całkowego na wskaźnikowy, jednak może być niebezpieczny, jeśli zostanie niewłaściwie użyty.
static_cast oferuje dobrą konwersję typów liczbowych, np. z wyliczeń na ints lub ints na zmiennoprzecinkowe lub dowolne typy danych, których jesteś pewny. Nie wykonuje żadnych kontroli w czasie wykonywania.
Natomiast dynamic_cast wykona te kontrole, oznaczając wszelkie niejednoznaczne przypisania lub konwersje. Działa tylko w przypadku wskaźników i referencji i powoduje obciążenie.
Jest kilka innych, ale są to główne, na które się natkniesz.
static_cast, oprócz manipulowania wskaźnikami do klas, może być również używany do wykonywania konwersji wyraźnie zdefiniowanych w klasach, a także do wykonywania standardowych konwersji między podstawowymi typami:
Dlaczego ktoś miałby pisać static_cast<int>(d), kiedy (int)djest o wiele bardziej zwięzły i czytelny? (Mam na myśli w przypadku typów podstawowych, a nie wskaźników obiektowych.)
Todd Lehman
@ gd1 - Dlaczego ktokolwiek miałby stawiać spójność ponad czytelnością? (właściwie pół poważny)
Todd Lehman
2
@ToddLehman: Mnie, biorąc pod uwagę, że tworzenie wyjątków dla niektórych typów tylko dlatego, że są one dla ciebie czymś wyjątkowym, nie ma dla mnie żadnego sensu, a także nie zgadzam się z twoją samą koncepcją czytelności. Krótszy nie oznacza, że jest bardziej czytelny, jak widzę na zdjęciu zamieszczonym w innym komentarzu.
gd1
2
static_cast to wyraźna i świadoma decyzja o dokonaniu bardzo szczególnego rodzaju konwersji. W ten sposób zwiększa jasność intencji. Jest również bardzo przydatny jako znacznik do wyszukiwania plików źródłowych w poszukiwaniu konwersji podczas przeglądu kodu, błędu lub aktualizacji.
Persixty
1
@ToddLehman kontrapunkt: Dlaczego ktoś miałby pisać, (int)dkiedy int{d}jest o wiele bardziej czytelny? Konstruktor, lub podobny do funkcji, jeśli masz (), składnia nie jest tak szybka, aby przekształcić się w koszmarny labirynt nawiasów w złożonych wyrażeniach. W takim przypadku byłoby to int i{d}zamiast int i = (int)d. O wiele lepiej IMO. To powiedziawszy, kiedy potrzebuję tymczasowego wyrażenia, używam static_casti nigdy nie używałem rzutów konstruktorów, nie sądzę. Używam tylko, (C)castsgdy pośpiesznie piszę debugowanie couts ...
Odpowiedzi:
Głównym powodem jest to, że klasyczne odlewy C nie dokonują rozróżnienia między tym, co nazywamy
static_cast<>()
,reinterpret_cast<>()
,const_cast<>()
, idynamic_cast<>()
. Te cztery rzeczy są zupełnie inne.A
static_cast<>()
jest zwykle bezpieczny. Istnieje poprawna konwersja w języku lub odpowiedni konstruktor, który to umożliwia. Jedynie raz jest to trochę ryzykowne, kiedy rzucisz się na dziedziczną klasę; musisz upewnić się, że obiekt jest w rzeczywistości potomkiem, o którym się twierdzi, że jest zewnętrzny w stosunku do języka (jak flaga w obiekcie). Adynamic_cast<>()
jest bezpieczne, dopóki wynik jest sprawdzany (wskaźnik) lub możliwy wyjątek jest uwzględniany (odniesienie).Z drugiej strony A
reinterpret_cast<>()
(lub aconst_cast<>()
) jest zawsze niebezpieczne. Mówisz kompilatorowi: „zaufaj mi: wiem, że to nie wyglądafoo
(wygląda na to, że nie można go modyfikować), ale tak jest”.Pierwszym problemem jest to, że prawie niemożliwe jest określenie, który wystąpi w obsadzie w stylu C, bez patrzenia na duże i rozproszone fragmenty kodu i znajomości wszystkich reguł.
Załóżmy, że:
Teraz te dwie są kompilowane w ten sam sposób:
Zobaczmy jednak prawie identyczny kod:
Jak widać, nie ma łatwego sposobu na rozróżnienie tych dwóch sytuacji bez wiedzy o wszystkich zaangażowanych klasach.
Drugi problem polega na tym, że rzutowanie w stylu C jest zbyt trudne do zlokalizowania. W złożonych wyrażeniach może być bardzo trudno zobaczyć rzutki w stylu C. Jest praktycznie niemożliwe napisanie zautomatyzowanego narzędzia, które musi zlokalizować rzutowania w stylu C (na przykład narzędzie wyszukiwania) bez pełnego interfejsu kompilatora C ++. Z drugiej strony łatwo jest wyszukać „static_cast <” lub „reinterpret_cast <”.
Oznacza to, że nie tylko rzuty w stylu C są bardziej niebezpieczne, ale znacznie trudniej jest znaleźć je wszystkie, aby upewnić się, że są prawidłowe.
źródło
static_cast
do odrzucania hierarchii dziedziczenia, ale raczejdynamic_cast
. Zwróci to wskaźnik zerowy lub prawidłowy wskaźnik.static_cast
w tej sytuacji.dynamic_cast
może być bezpieczniejszy, ale nie zawsze jest to najlepsza opcja. Czasami wiesz, że wskaźnik wskazuje na podtyp, nieprzezroczysty dla kompilatora, a astatic_cast
jest szybszy. W co najmniej niektórych środowiskachdynamic_cast
wymaga opcjonalnej obsługi kompilatora i kosztu działania (włączenie RTTI) i możesz nie chcieć włączać go tylko dla kilku kontroli, które możesz wykonać samodzielnie. RTTI C ++ jest tylko jednym możliwym rozwiązaniem problemu.static_cast
. Odpowiednikiem Creinterpret_cast
jest*(destination_type *)&
, tzn. Pobranie adresu obiektu, rzutowanie tego adresu na wskaźnik na inny typ, a następnie dereferencje. Z wyjątkiem przypadku typów znaków lub niektórych typów struktur, dla których C definiuje zachowanie tego konstruktu, generalnie powoduje to niezdefiniowane zachowanie w C.int
(iint
tylko), dlaczego użyciestatic_cast<int>
vs.(int)
jako jedyna korzyść wydaje się być ze zmiennymi klasowymi i wskaźnikami. Poproś o rozwinięcie tego.int
dynamic_cast
nie ma zastosowania, ale wszystkie pozostałe powody są ważne . Na przykład: powiedzmy, żev
jest parametrem funkcji zadeklarowanym jakofloat
, to(int)v
jeststatic_cast<int>(v)
. Ale jeśli zmienisz parametr nafloat*
,(int)v
po cichu staje sięreinterpret_cast<int>(v)
chwilowostatic_cast<int>(v)
nielegalne i poprawnie przechwytywane przez kompilator.Jedna pragmatyczna wskazówka: możesz łatwo wyszukać słowo kluczowe static_cast w kodzie źródłowym, jeśli planujesz uporządkować projekt.
źródło
int
parametrem.Przeczytaj więcej na temat:
Jaka jest różnica między rzutowaniem static_cast <> a rzutowaniem w stylu C
i
rzutowaniem regularnym vs. static_cast vs. dynamic_cast
źródło
static_cast<>()
jest to bardziej czytelne. Mam na myśli, że czasami tak jest, ale przez większość czasu - szczególnie na podstawowych typach liczb całkowitych - jest po prostu okropnie i niepotrzebnie gadatliwy. Na przykład: Jest to funkcja, która zamienia bajty 32-bitowego słowa.static_cast<uint##>()
Czytanie przy użyciu rzutów byłoby prawie niemożliwe , ale dość łatwe do zrozumienia przy użyciu(uint##)
rzutów. Zdjęcie kodu: imgur.com/NoHbGvealways
. (ale w większości przypadków tak) Są pewne przypadki, w których rzutowanie w stylu c jest znacznie bardziej czytelne. To jeden z powodów, dla których casting w stylu c jest wciąż aktywny i działa w imho c ++. :) Nawiasem mówiąc, był to bardzo ładny przykład(uint32_t)(uint8_t)
), aby osiągnąć, że bajty oprócz najniższych są resetowane. Do tego jest bitowe i (0xFF &
). Użycie obsad zaciemnia intencję.Pytanie jest większe niż tylko użycie rzutowania static_cast lub rzutowania w stylu C, ponieważ podczas korzystania z rzutów w stylu C zachodzą różne rzeczy. Operatory rzutowania w C ++ mają na celu uwydatnienie tych operacji.
Na powierzchni rzutowania w stylu static_cast i C wyglądają tak samo, na przykład podczas rzutowania jednej wartości na drugą:
Oba z nich podają wartość całkowitą na podwójną. Jednak podczas pracy ze wskaźnikami sprawy stają się bardziej skomplikowane. kilka przykładów:
W tym przykładzie (1) być może OK, ponieważ obiekt wskazywany przez A jest tak naprawdę instancją B. Ale co, jeśli nie wiesz w tym momencie kodu, na co właściwie wskazuje? (2) może całkowicie legalny (chcesz spojrzeć tylko na jeden bajt liczby całkowitej), ale może to być również błąd, w którym to przypadku błąd byłby miły, jak (3). Operatory rzutowania w C ++ mają na celu ujawnienie tych problemów w kodzie, zapewniając błędy kompilacji lub wykonania w miarę możliwości.
Zatem do ścisłego „rzutowania wartości” możesz użyć static_cast. Jeśli chcesz odlewania polimorficznego wskaźników w czasie wykonywania, użyj dynamic_cast. Jeśli naprawdę chcesz zapomnieć o typach, możesz użyć reintrepret_cast. Aby po prostu wyrzucić const przez okno, jest const_cast.
Po prostu uwydatniają kod, dzięki czemu wygląda na to, że wiesz, co robiłeś.
źródło
static_cast
oznacza, że nie możesz przypadkowoconst_cast
lubreinterpret_cast
, co jest dobrą rzeczą.źródło
Zobacz Efektywne C ++ Wprowadzenie
źródło
Chodzi o to, ile bezpieczeństwa typu chcesz narzucić.
Kiedy piszesz
(bar) foo
(co jest równoważne zreinterpret_cast<bar> foo
jeśli nie podałeś operatora konwersji typu), mówisz kompilatorowi, aby zignorował bezpieczeństwo typu, i po prostu rób to, co mu kazano.Kiedy piszesz
static_cast<bar> foo
, pytasz kompilatora, aby przynajmniej sprawdził, czy konwersja typu ma sens, a dla typów integralnych wstawił kod konwersji.EDYCJA 26.02.2014
Napisałem tę odpowiedź ponad 5 lat temu i pomyliłem się. (Patrz komentarze.) Ale wciąż zyskuje poparcie!
źródło
static_cast<bar>(foo)
w nawiasach. To samo dotyczyreinterpret_cast<bar>(foo)
.Rzutów w stylu C można łatwo przeoczyć w bloku kodu. Rzutowania w stylu C ++ są nie tylko lepszą praktyką; oferują znacznie większą elastyczność.
reinterpret_cast pozwala na konwersję typu całkowego na wskaźnikowy, jednak może być niebezpieczny, jeśli zostanie niewłaściwie użyty.
static_cast oferuje dobrą konwersję typów liczbowych, np. z wyliczeń na ints lub ints na zmiennoprzecinkowe lub dowolne typy danych, których jesteś pewny. Nie wykonuje żadnych kontroli w czasie wykonywania.
Natomiast dynamic_cast wykona te kontrole, oznaczając wszelkie niejednoznaczne przypisania lub konwersje. Działa tylko w przypadku wskaźników i referencji i powoduje obciążenie.
Jest kilka innych, ale są to główne, na które się natkniesz.
źródło
static_cast, oprócz manipulowania wskaźnikami do klas, może być również używany do wykonywania konwersji wyraźnie zdefiniowanych w klasach, a także do wykonywania standardowych konwersji między podstawowymi typami:
źródło
static_cast<int>(d)
, kiedy(int)d
jest o wiele bardziej zwięzły i czytelny? (Mam na myśli w przypadku typów podstawowych, a nie wskaźników obiektowych.)(int)d
kiedyint{d}
jest o wiele bardziej czytelny? Konstruktor, lub podobny do funkcji, jeśli masz()
, składnia nie jest tak szybka, aby przekształcić się w koszmarny labirynt nawiasów w złożonych wyrażeniach. W takim przypadku byłoby toint i{d}
zamiastint i = (int)d
. O wiele lepiej IMO. To powiedziawszy, kiedy potrzebuję tymczasowego wyrażenia, używamstatic_cast
i nigdy nie używałem rzutów konstruktorów, nie sądzę. Używam tylko,(C)casts
gdy pośpiesznie piszę debugowaniecout
s ...