Когда следует использовать static_cast, dynamic_cast, const_cast и reinterpret_cast?

Какое правильное использование:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C-стиль (type)value
  • Литье в стиле функции type(value)

Как вы решаете, что использовать в каких случаях?

1878
задан e.James 01 дек. '08 в 23:11
источник поделиться

6 ответов

static_cast - это первый бросок, который вы должны попытаться использовать. Он делает такие вещи, как неявные преобразования между типами (например, от int до float или указатель на void*), а также может вызывать явные функции преобразования (или неявные). Во многих случаях явно не указано static_cast, но важно отметить, что синтаксис T(something) эквивалентен (T)something, и его следует избегать (подробнее об этом позже). A T(something, something_else) является безопасным и, тем не менее, гарантированно вызывает конструктор.

static_cast также может выполняться через иерархии наследования. Это необязательно при бросании вверх (в сторону базового класса), но при отбрасывании вниз его можно использовать до тех пор, пока он не наследует наследование virtual. Однако проверка не выполняется, и поведение undefined с static_cast вниз по иерархии относится к типу, который на самом деле не является типом объекта.


const_cast можно использовать для удаления или добавления const к переменной; ни один другой С++-способ не способен удалить его (даже не reinterpret_cast). Важно отметить, что изменение ранее значения const является только undefined, если исходная переменная const; если вы используете его, чтобы удалить const ссылку на то, что не было объявлено с помощью const, это безопасно. Это может быть полезно, например, при перегрузке функций-членов на основе const. Его также можно использовать для добавления const к объекту, например, для вызова перегрузки функции-члена.

const_cast также работает аналогично на volatile, хотя это менее распространено.


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

dynamic_cast имеет некоторые ограничения. Он не работает, если в иерархии наследования есть несколько объектов одного типа (так называемый "страшный бриллиант" ), и вы не используете наследование virtual. Он также может проходить только через публичное наследование - он всегда будет нести через наследование protected или private. Это редко бывает проблемой, поскольку такие формы наследования встречаются редко.


reinterpret_cast является самым опасным броском, и его следует использовать очень экономно. Он превращает один тип непосредственно в другой - например, отбрасывая значение от одного указателя к другому или сохраняя указатель в int или всевозможные другие неприятные вещи. В основном, единственная гарантия, которую вы получаете с помощью reinterpret_cast, заключается в том, что обычно, если вы возвращаете результат обратно к исходному типу, вы получите то же самое значение (но не, если промежуточный тип меньше, чем оригинальный type). Существует ряд преобразований, которые не могут выполнять reinterpret_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 также игнорируют управление доступом при выполнении static_cast, что означает, что они имеют возможность выполнять операцию, которую не может выполнять другой актерский состав. Это, в основном, kludge, и, на мой взгляд, это еще одна причина, чтобы избежать приведения в стиле C.

2074
ответ дан coppro 01 дек. '08 в 23:26
источник поделиться

Используйте dynamic_cast для преобразования указателей/ссылок в иерархию наследования.

Используйте static_cast для конверсий обычного типа.

Используйте reinterpret_cast для низкоуровневого переинтерпретации битовых шаблонов. Используйте с особой осторожностью.

Используйте const_cast для отбрасывания const/volatile. Избегайте этого, если вы не застряли с использованием некорректного API.

249
ответ дан Fred Larson 01 дек. '08 в 23:22
источник поделиться

(Множество теоретических и концептуальных объяснений было дано выше)

Ниже приведены некоторые из практических примеров, когда я использовал 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);
}
129
ответ дан Sumit Arora 21 янв. '14 в 7:53
источник поделиться

Это может помочь, если вы знаете немного внутренних дел...

static_cast

  • Компилятор С++ уже знает, как преобразовать типы масштабирования, такие как float to int. Используйте static_cast для них.
  • В общем, при преобразовании типа A в B static_cast будет вызывать конструктор B, передающий его A. Если B не имеет такого конструктора, тогда вы получаете ошибку времени компиляции.
  • Передача от A* до B* всегда выполняется, если A и B находятся в иерархии наследования (или void), иначе вы получите ошибку компиляции.
  • Gotcha. Если вы указали базовый указатель на производный указатель, но если фактический объект - это не производный тип, то вы не получите ошибку. Вы получаете плохой указатель, и как только вы пытаетесь получить доступ к членам производного указателя, вы получаете segfault во время выполнения.
  • То же самое относится к A& к B&.
  • Gotcha: ролик от Derived to Base или наоборот предлагает создать новую копию! Для людей, приезжающих с С#/Java, многие из них могут быть огромным сюрпризом.

dynamic_cast

  • dynamic_cast использует информацию типа времени выполнения, чтобы выяснить, действительна ли акция. Например, (Base*) to (Derived*) может завершиться ошибкой, если указатель не является фактически производным типом.
  • Это означает, что dynamic_cast очень дорого по сравнению с static_cast!
  • Для A* - B*, если приведение недействительно, тогда dynamic_cast вернет nullptr.
  • Для A& - B&, если приведение недействительно, тогда dynamic_cast выдает исключение bad_cast.

const_cast

  • В то время как static_cast может выполнять не const const, он не может идти другим путем. Константа может выполнять оба способа.
  • Одним из примеров, когда это удобно, является итерация через некоторый контейнер, такой как set<T>, который возвращает только его элементы как const, чтобы убедиться, что вы не меняете его ключ. Однако, если ваше намерение состоит в том, чтобы изменить объект, не являющийся ключом, тогда он должен быть в порядке. Вы можете использовать const_cast для удаления константы.
  • Другим примером является реализация T& foo(), а также const T& foo(). Чтобы избежать дублирования кода, вы можете применить const_cast для возврата значения одной функции из другой.
  • В отличие от вышеприведенных двух бросков, накладные расходы времени выполнения.

reinterpret_cast

  • В основном это говорит о том, что эти байты берут в этой ячейке памяти и рассматривают ее как заданный объект.
  • Например, вы можете загрузить 4 байта float в 4 байта int, чтобы увидеть, как выглядят биты в float.
  • Очевидно, что если данные не соответствуют типу, вы можете получить segfault.
  • Для этого лифта нет служебных данных во время выполнения.
32
ответ дан ShitalShah 11 дек. '16 в 5:05
источник поделиться

Помогает ли это ответить на ваш вопрос?

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

10
ответ дан andreas buykx 01 дек. '08 в 23:20
источник поделиться

В дополнение к остальным ответам до сих пор здесь неочевидный пример, где 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
ответ дан Serge Rogatch 31 мая '15 в 17:16
источник поделиться

Другие вопросы по меткам