Як static_cast, так і reinterpret_cast, здається, добре спрацьовують для перенесення недійсних * до іншого типу вказівника. Чи є вагомі причини надати перевагу одне над іншим?
Як static_cast, так і reinterpret_cast, здається, добре спрацьовують для перенесення недійсних * до іншого типу вказівника. Чи є вагомі причини надати перевагу одне над іншим?
Відповіді:
Використанняstatic_cast
: саме вузький ролик точно описує те, що тут здійснюється перетворення.
Існує помилкова думка, що використання reinterpret_cast
буде краще відповідати, оскільки це означає "повністю ігнорувати безпеку типу та просто передати з А на В".
Однак це насправді не описує ефект а reinterpret_cast
. Скоріше, reinterpret_cast
має низку значень, для всіх яких встановлено, що "відображення, яке виконується, визначено reinterpret_cast
реалізацією". [5.2.10.3]
Але в даному конкретному випадку виливки з void*
до T*
відображенню цілком добре визначено стандартом; а саме: призначити тип безтиповому вказівнику без зміни його адреси.
Це причина віддати перевагу static_cast
.
Крім того, і, мабуть, важливішим є той факт, що будь-яке використання reinterpret_cast
прямо небезпечно, оскільки воно перетворює що-небудь у будь-що інше насправді (для покажчиків), хоча воно static_cast
є набагато більш обмежуючим, забезпечуючи тим самим кращий рівень захисту. Це вже врятувало мене від помилок, де я випадково намагався примусити один тип вказівника до іншого.
Це складне питання. З одного боку, Конрад робить чудовий пункт щодо визначення специфікації для reinterpret_cast , хоча на практиці це, мабуть, робить те ж саме. З іншого боку, якщо ви робите кастинг між типами покажчиків (як це досить часто при індексації в пам'яті за допомогою char *, наприклад), static_cast призведе до помилки компілятора, і ви все одно будете змушені використовувати reinterpret_cast .
На практиці я використовую reinterpret_cast, тому що це більше описує наміри операції виклику. Ви, безумовно, можете зробити так, щоб інший оператор призначив лише вказівник реінтерпретації (що гарантувало повернення тієї ж адреси), але в стандарті немає жодної.
reinterpret_cast
!
Я пропоную завжди використовувати найслабший акторський склад.
reinterpret_cast
може використовуватися для наведення покажчика на a float
. Чим більше в структурі складів, тим більше уваги потребує його використання.
У випадку char*
я б застосував c-style casting, поки у нас його немає reinterpret_pointer_cast
, тому що він слабший і нічого іншого недостатньо.
float f = *reinterpret_cast<const float*>(&p);
float
, що хибно. Вираз закидання void **
до const float *
, а потім використовує операцію разименованія (який не є виливок), щоб конвертувати const float *
в float
.
Мої особисті переваги засновані на кодовій грамотності на зразок цього:
void* data = something;
MyClass* foo = reinterpret_cast<MyClass*>(data);
foo->bar();
або
typedef void* hMyClass; //typedef as a handle or reference
hMyClass = something;
const MyClass& foo = static_cast<MyClass&>(*hMyClass);
foo.bar();
Вони обидва роблять те саме в кінцевому рахунку, але static_cast здається більш доцільним у середньому посуді, оточенні додатків, тоді як реінтерпретований склад здається більше схожим на те, що ви бачите в IMHO бібліотеці нижчого рівня.