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


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

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

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


2023


источник


Ответы:


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

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


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


C-стиль а также функциональный стиль являются отливки, использующие (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.


2183



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

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

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

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


276



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

Ниже приведены некоторые из практические примеры когда я использовал 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);
}

147



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

static_cast

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

dynamic_cast

  • dynamic_cast использует информацию типа времени выполнения, чтобы выяснить, действителен ли кадр. Например, (Base*)в (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.
  • Для этого приведения нет времени выполнения.

48



Есть ли это ответьте на свой вопрос?

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


11



In addition to the other answers so far, here is unobvious example where static_cast is not sufficient so that reinterpret_cast is needed. Suppose there is a function which in an output parameter returns pointers to objects of different classes (which do not share a common base class). A real example of such function is CoCreateInstance() (see the last parameter, which is in fact void**). Suppose you request particular class of object from this function, so you know in advance the type for the pointer (which you often do for COM objects). In this case you cannot cast pointer to your pointer into void** with static_cast: you need reinterpret_cast<void**>(&yourPointer).

In code:

#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) );

However, static_cast works for simple pointers (not pointers to pointers), so the above code can be rewritten to avoid reinterpret_cast (at a price of an extra variable) in the following way:

#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