Питання Коли слід використовувати static_cast, dynamic_cast, const_cast та reinterpret_cast?


Які належні способи використання:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C-style cast (type)value
  • Функціональний стиль відтворення type(value)

Як хтось вирішить, який користуватися, в якому конкретному випадку?


2052
2017-12-01 20:11


походження


Може бути хороший довідник тут:Як ви пояснюєте відмінності між static_cast, reinterpret_cast, const_cast та dynamic_cast для нового програміста C ++?. - Nan Xiao
Для деяких корисних конкретних прикладів використання різних видів лит, ви можете перевірити першу відповідь на подібне питання в ця інша тема. - TeaMonkie


Відповіді:


static_cast це перший комір, який ви повинні намагатися використовувати. Це робить речі, як неявні перетворення між типами (наприклад, int до float, або вказівка ​​на void*), а також може викликати явні функції перетворення (або неявні). У багатьох випадках чітко вказується static_cast не треба, але важливо відзначити, що T(something) синтаксис еквівалентний (T)something і його слід уникати (докладніше про це пізніше). А. T(something, something_else) це безпечно, однак, і гарантовано зателефонувати конструктору.

static_cast також може передавати через ієрархії спадщини. Це не є необхідним, якщо відкидати вгору (у напрямку до базового класу), але при відліку вниз його можна використовувати до тих пір, поки він не пройде через virtual спадщина Однак це не робить перевірки, і це невизначена поведінка static_cast вниз ієрархія до типу, який насправді не є типом об'єкта.


const_cast може бути використаний для видалення або додавання const до змінної; жоден інший програвач C ++ не може видалити його (навіть не reinterpret_cast) Важливо відзначити, що модифікація раніше const значення є лише невизначеним, якщо вихідна змінна є const; якщо ви використовуєте це, щоб взяти const вимкніть посилання на те, що не було оголошено const, це безпечно. Це може бути корисним при перевантаженні функцій-членів на основі const, наприклад. Його також можна використовувати для додавання const до об'єкта, наприклад, для виклику перевантаження функції-члена.

const_cast також працює точно так само volatile, хоча це рідше.


dynamic_cast використовується майже виключно для обробки поліморфізму. Ви можете передати вказівник або посилання на будь-який поліморфний тип на будь-який інший тип класу (поліморфний тип має щонайменше одну віртуальну функцію, оголошену або успадковану). Ви можете використовувати це для більш ніж просто лиття вниз - ви можете кинути боком або навіть зробити іншу ланцюжок. The dynamic_cast буде шукати потрібний об'єкт і повернути його, якщо можливо. Якщо це не може, він повернеться nullptr у разі покажчика або кидка std::bad_cast у випадку посилання.

dynamic_cast однак має деякі обмеження. Це не спрацьовує, якщо в ієрархії спадщини є кілька об'єктів одного типу (так званий "страшний алмаз"), і ви не використовуєте virtual спадщина Це також може пройти лише через суспільну спадщину - воно завжди не зможе подорожувати protected або private спадщина Однак це рідко виникає, оскільки такі форми спадщини бувають рідко.


reinterpret_cast це найнебезпечніший випадок, і його слід використовувати дуже економно. Він перетворює один тип безпосередньо в інший - наприклад, відкидає значення від одного покажчика до іншого або зберігає вказівник у int, або всілякі інші неприємні речі. Багато в чому, єдина гарантія, яку ви отримуєте reinterpret_cast це, як правило, якщо ви повернете результат до вихідного типу, ви отримаєте точне значення (але ні якщо проміжний тип менше, ніж вихідний тип). Існує безліч конверсій reinterpret_cast не може робити теж. Він використовується, перш за все, для особливо дивних перетворень та бітних маніпуляцій, таких як перетворення вихідного потоку даних у фактичні дані або зберігання даних у низьких бітах вирівняного покажчика.


C-style cast і функціональний стиль cast є лиття з використанням (type)object або type(object), відповідно. Кастинг C-стилю визначається як перший з наступних, який вдається:

  • const_cast
  • static_cast (хоча ігноруючи обмеження доступу)
  • static_cast (див. вище), потім const_cast
  • reinterpret_cast
  • reinterpret_cast, потім const_cast

Таким чином, він може використовуватися в якості заміни для інших відходів у деяких випадках, але може бути надзвичайно небезпечним через здатність перейти до reinterpret_cast, і останній повинен бути кращим, коли потрібне явне відливання, якщо ви не впевнені static_cast досягне успіху або reinterpret_cast вийде з ладу Навіть тоді розглянемо більш тривалий, більш явний варіант.

C-style також відхиляє контроль доступу при виконанні a static_cast, що означає, що вони мають можливість виконувати операцію, яку ніхто інший не може. Це, в основному, хладже, хоча і, на мій погляд, є ще однією причиною, щоб уникнути звільнення C-стилю.


2211
2017-12-01 20:22



dynamic_cast тільки для поліморфних типів. вам потрібно використовувати його лише тоді, коли ви переходите до похідного класу. static_cast, безумовно, є першим варіантом, якщо вам спеціально не потрібна функціональність dynamic_cast. Це взагалі не яке-небудь дивовижне срібне куле "перевірка типу". - jalf
Відмінна відповідь! Одне швидке зауваження: static_cast може знадобитися для відтворення ієрархії у випадку, якщо у вас є похідний * &, щоб відкинути в Base * &, так як подвійні вказівники / посилання не автоматично передають ієрархію. Дві хвилини тому я зіткнувся з такою (відверто, незвичайною) ситуацією. ;-) - bartgol
* "ніякий інший C ++ cast не здатний видалити const (навіть не reinterpret_cast) "... насправді? А як? reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))? - Mehrdad
Я думаю, що важливою недоступною деталлю є те, що dynamic_cast має штраф за виконання часу, ніж статичний або reinterpret_cast. Це важливо, наприклад, в режимі реального часу програмне забезпечення. - jfritz42
Можливо варто згадати це reinterpret_cast часто є зброєю вибору при роботі з безліччю непрозорих типів даних API - camelCase


Використовуйте dynamic_cast для перетворення покажчиків / посилань у межах ієрархії успадкування.

Використовуйте static_cast для звичайних перетворень типу.

Використовуйте reinterpret_cast для низькорівневого переосмислення бітових візерунків. Використовувати з особливою обережністю.

Використовуйте const_cast для того, щоб відкинути const/volatile. Уникайте цього, якщо ви не застрягли, використовуючи const-некоректний API.


284
2018-01-21 04:53





(Багато теоретичних та концептуальних пояснень було викладено вище) 

Нижче наведено деякі з них практичні приклади коли я використовував static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Згадайте це також, щоб зрозуміти пояснення: http://www.cplusplus.com/doc/tutorial/typecasting/)

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

152
2017-12-11 02:05



Теорія деяких інших відповідей є добрим, але все ж таки незрозумілою, бачачи ці приклади після прочитання інших відповідей дійсно робить їх усі сенсу. Ось без прикладів, я все ще не впевнений, але з ними я зараз впевнений у тому, що означають інші відповіді. - Solx
Про останнє використання reinterpret_cast: чи не так, як з використанням static_cast<char*>(&val) ? - Lorenzo Belli
@ ЛорензоБеллі Звичайно, ні. Ти спробував це? Останній недійсний C ++ і блокує компіляцію. static_cast Працює лише між типами з визначеними конверсіями, видимим співвідношенням по спадщині або з / з void *. Для всього іншого існують інші лиття. reinterpret cast до будь-якого char * тип дозволений для читання представлення будь-якого об'єкта - і один з єдиних випадків, коли це ключове слово корисно, а не нестримний генератор implementation- / undefined поведінки. Але це не вважається "нормальним" перетворенням, тому не допускається (звичайно) дуже консервативним static_cast. - underscore_d
reinterpret_cast досить поширений, коли ви працюєте з системним програмним забезпеченням, таким як бази даних. У більшості випадків ви пишете власного менеджера сторінок, який не має уявлення про те, який тип даних зберігається на сторінці, і просто повертає порожній покажчик. Його до більш високих рівнів зробити переосмислення відлиття і вивести його так, як хочеться. - Sohaib


Це може допомогти, якщо ви знаєте трохи внутрішніх ...

static_cast

  • Компілятор C ++ вже знає, як конвертувати типи скалірувальника, наприклад, float в int. Використовуйте static_cast для них.
  • Загалом, при перетворенні типу A на B, static_cast може викликати конструктор B, який передає його A. Якщо B не має такого конструктора, ви отримаєте помилку часу компіляції.
  • Литий з A* до B* завжди вдається, якщо A та B знаходяться в ієрархії успадкування (або недійсні), інакше ви отримаєте помилку компіляції.
  • Готча: Якщо ви вказали базовий покажчик на похідний покажчик, але якщо фактичний об'єкт є, якщо не похідним типом, то ви ні отримати помилку Ви отримуєте поганий покажчик, і як тільки ви намагаєтеся отримати доступ до учасників похідного покажчика, ви отримуєте сегрегацію під час виконання.
  • Те саме стосується і A& до B&.
  • Готча: Cast від Derived to Base або viceversa створює нову копію! Для людей, які надходять з C # / Java, багато хто з перерахованих вище може стати величезним сюрпризом.

dynamic_cast

  • dynamic_cast використовує інформацію про тип runtime, щоб з'ясувати, чи є дійсним cast. Наприклад, (Base*) до (Derived*)може зазнати невдачі, якщо покажчик фактично не має похідного типу.
  • Це означає, що dynamic_cast є дуже дорогим порівняно з static_cast!
  • Для A* до B*, якщо cast недійсний, then dynamic_cast поверне nullptr.
  • Для A& до B& якщо cast недійсний, then dynamic_cast викине bad_cast винятком.
  • На відміну від інших викидів, є накладні витрати на виконання.

const_cast

  • Хоча static_cast може робити неконстант для const, він не може йти іншим способом. Const_cast може виконувати обидва способи.
  • Один з прикладів, де це приносить корисно, повторюється через такий контейнер, як set<T> який повертає елементи як const, щоб переконатися, що ви не змінюєте його ключ. Однак, якщо ви маєте намір змінювати не ключові елементи об'єкта, то це має бути добре. Ви можете використовувати const_cast для видалення constness.
  • Інший приклад - це час, коли ви хочете реалізувати T& foo() так добре як const T& foo(). Щоб уникнути копіювання коду, ви можете застосувати const_cast для повернення значення однієї функції з іншого.

reinterpret_cast

  • Це в основному говорить, що візьміть ці байти до цієї пам'яті і подумайте про це як заданий об'єкт.
  • Наприклад, ви можете завантажити 4 байти поплавці до 4 байтів int, щоб побачити, наскільки біти в поплавці виглядають.
  • Очевидно, якщо дані неправильні для типу, ви можете отримати segfault.
  • Для цього режисура нема накладних витрат.

52
2017-12-01 20:20



Останній момент про const_cast невірно: тільки dynamic_cast насправді має накладні витрати часу. Схоже, ви, можливо, змінили порядок і забули перемістити та переформулювати це. - rubenvb
Ви правильні! Виправлено. - ShitalShah


Чи це відповісти на ваше запитання?

Я ніколи не користувався reinterpret_cast, і задаються питанням, чи працює в тому випадку, коли це потрібно, це не запах поганого дизайну. У коді базі я працюю dynamic_cast використовується багато. Різниця з static_cast це те, що a dynamic_cast чи перевірка виконання часу, яка може (безпечніше), або не може (більше накладні витрати) бути тим, що ви хочете (див msdn)


11
2018-05-31 14:16



Я використовував reintrepret_cast для одних цілей - отримувати біти з подвійного (такий же розмір довгий на моїй платформі). - Joshua
Reinterpret_cast потрібно, наприклад, для роботи з COM-об'єктами. CoCreateInstance () має вихідний параметр типу void ** (останній параметр), в якому ви передасте свій покажчик, оголошений як, наприклад, "INetFwPolicy2 * pNetFwPolicy2". Для цього вам потрібно написати щось на кшталт reinterpret_cast <void **> (& pNetFwPolicy2). - Serge Rogatch


На додаток до інших відповідей на сьогоднішній день, ось непомітний приклад, де static_cast недостатньо, щоб reinterpret_cast потрібен. Припустимо, що існує функція, яка у вихідному параметрі повертає покажчики на об'єкти різних класів (які не мають спільного базового класу). Справжній приклад такої функції є CoCreateInstance() (див. останній параметр, який насправді є void**) Припустімо, що ви запитуєте певний клас об'єкта за допомогою цієї функції, щоб заздалегідь знати тип для покажчика (який ви часто виконуєте для COM-об'єктів). У цьому випадку ви не можете перемістити вказівник на ваш вказівник void** з static_cast: тобі потрібно reinterpret_cast<void**>(&yourPointer).

В коді:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

Однак static_cast працює для простих вказівників (не вказівників на покажчики), тому вищезазначений код можна переписати, щоб уникнути reinterpret_cast (за ціною додаткової змінної) наступним чином:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

9