Wydaje się, że zarówno static_cast, jak i reinterpret_cast działają dobrze w przypadku rzutowania void * na inny typ wskaźnika. Czy istnieje dobry powód, aby faworyzować jednego nad drugim?
Wydaje się, że zarówno static_cast, jak i reinterpret_cast działają dobrze w przypadku rzutowania void * na inny typ wskaźnika. Czy istnieje dobry powód, aby faworyzować jednego nad drugim?
Odpowiedzi:
Zastosowaniestatic_cast
: jest to najwęższa obsada, która dokładnie opisuje dokonaną tutaj konwersję.
Istnieje błędne przekonanie, że użycie reinterpret_cast
byłoby lepsze, ponieważ oznacza „całkowicie zignoruj bezpieczeństwo typu i po prostu rzuć z A na B”.
Jednak tak naprawdę nie opisuje to efektu reinterpret_cast
. Raczej reinterpret_cast
ma wiele znaczeń, z których wszystkie utrzymują, że „wykonywane mapowanie reinterpret_cast
jest zdefiniowane w ramach implementacji”. [5.2.10.3]
Ale w szczególności przypadku odlewania z void*
do T*
mapowania jest zupełnie dobrze zdefiniowane przez standard; mianowicie, aby przypisać typ do wskaźnika bez typu, bez zmiany jego adresu.
To jest powód, aby preferować static_cast
.
Ponadto, i prawdopodobnie ważniejsze, jest fakt, że każde użycie reinterpret_cast
jest wręcz niebezpieczne, ponieważ przekształca wszystko w cokolwiek innego (w przypadku wskaźników), podczas gdy static_cast
jest znacznie bardziej restrykcyjne, zapewniając w ten sposób lepszy poziom ochrony. To już uratowało mnie przed błędami, w których przypadkowo próbowałem zmusić jeden typ wskaźnika do drugiego.
To trudne pytanie. Z jednej strony Konrad doskonale rozumie definicję specyfikacji dla reinterpret_cast , chociaż w praktyce prawdopodobnie robi to samo. Z drugiej strony, jeśli używasz rzutowania między typami wskaźników (co jest dość powszechne, na przykład podczas indeksowania w pamięci za pomocą znaku char *), static_cast wygeneruje błąd kompilatora i będziesz zmuszony użyć reinterpret_cast .
W praktyce używam reinterpret_cast, ponieważ jest to bardziej opisowe zamiar operacji rzutowania. Z pewnością możesz uzasadnić, że inny operator wyznaczy tylko reinterpretację wskaźnika (co gwarantowało zwrot tego samego adresu), ale nie ma takiego w standardzie.
reinterpret_cast
!
Zawsze sugeruję używanie najsłabszej możliwej obsady.
reinterpret_cast
może być użyty do rzutowania wskaźnika na float
. Im bardziej rzutuje się na strukturę, tym więcej uwagi wymaga jej użycie.
W przypadku char*
używałbym rzutowania w stylu c, dopóki nie będziemy mieli reinterpret_pointer_cast
, ponieważ jest on słabszy i nic więcej nie jest wystarczające.
float f = *reinterpret_cast<const float*>(&p);
float
, co jest fałszem. Odlewy ekspresyjne void **
do const float *
, a następnie używa operację wyłuskiwania (co nie jest odlew), aby przekształcić const float *
się float
.
Moje osobiste preferencje oparte są na umiejętności pisania kodu:
void* data = something;
MyClass* foo = reinterpret_cast<MyClass*>(data);
foo->bar();
lub
typedef void* hMyClass; //typedef as a handle or reference
hMyClass = something;
const MyClass& foo = static_cast<MyClass&>(*hMyClass);
foo.bar();
Oba robią to samo na końcu, ale static_cast wydaje się bardziej odpowiednie w środowisku aplikacji, podczas gdy reinterpretacja obsady wydaje się bardziej jak coś, co można zobaczyć w bibliotece niższego poziomu IMHO.