Я чув, що цю static_cast
функцію слід віддавати перевагу стилю С або простому кастингу в стилі функції. Це правда? Чому?
Я чув, що цю static_cast
функцію слід віддавати перевагу стилю С або простому кастингу в стилі функції. Це правда? Чому?
Відповіді:
Основна причина полягає в тому , що класичні зліпки C не роблять жодної різниці між тим, що ми називаємо static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
і dynamic_cast<>()
. Ці чотири речі абсолютно різні.
A, static_cast<>()
як правило, безпечний. Існує дійсна конверсія в мові або відповідний конструктор, що робить це можливим. Єдиний раз, коли це трохи ризиковано - це коли ти перейшов до спадкового класу; ви повинні переконатися, що об’єкт насправді є нащадком, який ви стверджуєте, що він є, за допомогою зовнішньої мови (як прапор об’єкта). A dynamic_cast<>()
безпечний, доки перевіряється результат (покажчик) або враховується можливий виняток (посилання).
А reinterpret_cast<>()
(або а const_cast<>()
) з іншого боку - це завжди небезпечно. Ви скажете компілятору: "повірте мені: я знаю, це не схоже на foo
(це виглядає так, ніби він не змінюється), але це так".
Перша проблема полягає в тому, що майже неможливо сказати, який з них відбуватиметься у складі C-стилю, не дивлячись на великі та розбіжні фрагменти коду та не знаючи всіх правил.
Припустимо наступне:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Тепер ці два складені однаково:
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
Однак подивимось цей майже однаковий код:
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!!!
Як бачите, не існує простого способу розрізнити дві ситуації, не знаючи багато про всі заняття.
Друга проблема полягає в тому, що ролі в стилі С занадто важко знайти. У складних виразах можна дуже важко побачити ролі в стилі С. Практично неможливо написати автоматизований інструмент, який повинен знаходити касти у стилі C (наприклад, інструмент пошуку) без повного виду компілятора C ++. З іншого боку, легко знайти "static_cast <" або "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.
Це означає, що ролі в стилі С не тільки небезпечніші, але набагато складніше знайти їх усіх, щоб переконатися в правильності їх виконання.
static_cast
для скидання спадкової ієрархії, а навпаки dynamic_cast
. Це поверне або нульовий покажчик, або дійсний вказівник.
static_cast
у цій ситуації. dynamic_cast
може бути безпечнішим, але це не завжди найкращий варіант. Іноді ви знаєте, що вказівник вказує на даний підтип, непрозорий для компілятора, а a static_cast
швидше. Принаймні в деяких середовищах dynamic_cast
потрібна додаткова підтримка компілятора та вартість виконання (увімкнення RTTI), і ви, можливо, не захочете включити його лише за пару перевірок, які ви можете зробити самостійно. RTTI C ++ - це лише одне можливе рішення проблеми.
static_cast
. Еквівалент С - reinterpret_cast
це *(destination_type *)&
взяття адреси об'єкта, перекидання цієї адреси на вказівник на інший тип, а потім перенаправлення. За винятком випадків, в разі символьних типів або певних типів структури , для яких С визначає поведінку цієї конструкції, це зазвичай призводить до непередбачуваного поведінки в C.
int
(і int
поодинці), чому використання static_cast<int>
порівняно (int)
як єдиної переваги, здається, має змінні та покажчики класів. Попросіть детально розібратися з цим.
int
dynamic_cast
не застосовується, але всі інші причини стоять. Наприклад: скажімо v
, параметр функції оголошено як float
, тоді (int)v
є static_cast<int>(v)
. Але якщо ви зміните параметр на float*
, (int)v
тихо стає, reinterpret_cast<int>(v)
поки static_cast<int>(v)
це незаконно і правильно потрапив компілятор.
Одна прагматична порада: ви можете легко шукати ключове слово static_cast у своєму вихідному коді, якщо плануєте налагодити проект.
int
параметром.
Коротше кажучи :
static_cast<>()
дає змогу перевіряти час компіляції, C-Style cast не робить.static_cast<>()
їх можна легко помітити в будь-якому місці всередині вихідного коду C ++; на відміну, C_Style ролі важче помітити.- Наміри передаються набагато краще, використовуючи ролі C ++.
Більше пояснення :
Статичний формат виконує перетворення між сумісними типами . Він схожий на ролі в стилі С, але є більш обмежуючим. Наприклад, команда в стилі C дозволить цілому вказівнику вказувати на знак.
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
Оскільки це призводить до того, що 4-байтний вказівник вказує на 1 байт виділеної пам'яті, запис у цей покажчик або спричинить помилку під час виконання, або замінить деяку суміжну пам'ять.
*p = 5; // run-time error: stack corruption
На відміну від набору C-стилю, статичний виступ дозволить компілятору перевірити, чи типи даних вказівника та покажчика сумісні, що дозволяє програмісту вловлювати це неправильне призначення вказівника під час компіляції.
int *q = static_cast<int*>(&c); // compile-time error
Детальніше про:
Яка різниця між static_cast <> та литтям у стилі C
та
Регулярним складанням проти static_cast проти динамичного_cast
static_cast<>()
це читабельніше. Я маю на увазі, іноді так і є, але більшість часу - особливо це стосується основних цілих типів - це просто жахливо і непотрібно багатослівне. Наприклад: Це функція, яка замінює байти 32-бітного слова. Це було б майже неможливо читати, використовуючи static_cast<uint##>()
касти, але це досить легко зрозуміти, використовуючи (uint##)
касти. Зображення коду: imgur.com/NoHbGve
always
теж не сказав . (але більшість випадків так) Є певні випадки, коли акторський стиль стилю набагато читає. Це одна з причин, коли кастинг стилю досі живе і б'є в c ++ imho. :) До речі, це був дуже приємний приклад
(uint32_t)(uint8_t)
), щоб досягти скидання байтів, окрім найнижчих. Для цього є порозрядне і ( 0xFF &
). Використання ролях заперечує наміри.
Питання є більш простим, ніж просто використання камер в стилі static_cast або C, оскільки різні випадки трапляються при використанні кастингу в стилі C. Оператори лиття C ++ мають на меті зробити ці операції більш чіткими.
На поверхні static_cast та C стилі відображаються однаково, наприклад, при передачі одного значення іншому:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Обидва вони приводять ціле значення до подвійного. Однак при роботі з покажчиками справи ускладнюються. кілька прикладів:
class A {};
class B : public A {};
A* a = new B;
B* b = (B*)a; //(1) what is this supposed to do?
char* c = (char*)new int( 5 ); //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error
У цьому прикладі (1), можливо, гаразд, тому що об'єкт, на який вказує A, є дійсно екземпляром B. Але що робити, якщо ви не знаєте в той момент коду, що насправді вказує? (2) може бути абсолютно законним (ви хочете подивитися лише на один байт цілого числа), але це також може бути помилкою, в цьому випадку помилка була б непоганою, як (3). Оператори кастингу C ++ призначені для викриття цих проблем у коді, надаючи помилки під час компіляції або час виконання під час виконання.
Отже, для суворого "кастингу значення" можна використовувати static_cast. Якщо ви хочете виконувати поліморфне лиття покажчиків під час виконання, використовуйте динамічну передачу. Якщо ви дійсно хочете забути про типи, ви можете використовувати reintrepret_cast. А щоб просто викинути const у вікно, є const_cast.
Вони просто роблять код більш явним, щоб він виглядав так, що ви знаєте, що робили.
static_cast
означає, що ви не можете випадково const_cast
або reinterpret_cast
, що добре.
Йдеться про те, яку безпеку типу ви хочете нав'язати.
Коли ви пишете (bar) foo
(що еквівалентноreinterpret_cast<bar> foo
тому, що ви не надали оператора перетворення типів), ви кажете компілятору ігнорувати безпеку типу та просто виконайте так, як йому сказано.
Коли ви пишете, static_cast<bar> foo
ви просите компілятора хоча б перевірити, чи є сенс перетворення типу, а для цілісних типів - вставити якийсь код перетворення.
EDIT 2014-02-26
Я написав цю відповідь більше 5 років тому, і я зрозумів її неправильно. (Див. Коментарі.) Але це все ще отримує відгуки!
static_cast<bar>(foo)
, з дужками. Те саме для reinterpret_cast<bar>(foo)
.
У ролях коду легко пропустити касти в стилі. У ролях стилів C ++ - це не лише краща практика; вони пропонують набагато більшу ступінь гнучкості.
reinterpret_cast дозволяє інтегрувати конверсії типу вказівника, проте при неправильному використанні може бути небезпечним.
static_cast пропонує хорошу конверсію для числових типів, наприклад, від перерахунків до int чи int до плаваючих чи будь-яких типів даних, яким ви впевнені. Він не виконує жодних перевірок часу виконання.
З іншого боку, динамічна передача виконає ці перевірки, позначивши будь-які неоднозначні завдання чи перетворення. Він працює лише за покажчиками та посиланнями і несе накладні витрати.
Є кілька інших, але це головні, з якими ви зіткнетесь.
static_cast, крім маніпулювання покажчиками на класи, також може використовуватися для виконання перетворень, чітко визначених у класах, а також для здійснення стандартних перетворень між основними типами:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
, коли (int)d
це набагато більш стисло і читабельно? (Я маю на увазі у випадку основних типів, а не на об’єктні покажчики.)
(int)d
коли int{d}
набагато читає? Конструктор, або подібний до функцій ()
, синтаксис не так швидко перетворюється на кошмарний лабіринт дужок у складні вирази. У цьому випадку це було б int i{d}
замість int i = (int)d
. Набагато краще ІМО. Однак, коли мені просто потрібен тимчасовий вираз, я використовую static_cast
і ніколи не використовую конструкторські роли, я не думаю. Я використовую лише (C)casts
тоді, коли поспішаю писати налагодження cout
s ...