Я чув, що цю 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тоді, коли поспішаю писати налагодження couts ...