Когда следует использовать статическое литье, динамическое литье, const cast и reinterpret cast?

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

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

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

7 ответов


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

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


const_cast можно использовать для удаления или добавления const для переменная; никакой другой c++ cast не способен ее удалить (даже 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 и


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

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

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

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


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

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

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

метод static_cast

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

операцию dynamic_cast

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

const_cast

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

оператора reinterpret_cast

  • это в основном говорит, что возьмите эти байты в этом месте памяти и подумайте об этом как о данном объекте.
  • например, вы можете загрузить 4 байта float до 4 байтов int, чтобы увидеть, как выглядят биты в float.
  • очевидно, что если данные неверны для типа, вы можете получить segfault.
  • нет накладными расходами за этот бросок.

тут этой ответить на ваш вопрос?

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


в дополнение к другим ответам до сих пор, вот неочевидный пример, где static_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);

В то время как другие ответы хорошо описали все различия между кастами C++, я хотел бы добавить короткую заметку, почему вы не должны использовать касты C-style (Type) var и Type(var).

для начинающих C++ C-style casts выглядят как операция над суперсетом над c++ casts (static_cast (), dynamic_cast (), const_cast (), reinterpret_cast ()), и кто-то может предпочесть их над c++ casts. На самом деле C-style cast является надмножеством и короче для записи.

главная проблема C-style casts заключается в том, что они скрывают реальное намерение разработчика. Приведения в стиле C могут выполнять практически все типы приведений из обычно безопасных приведений, выполняемых static_cast () и dynamic_cast () в потенциально опасные приведения, такие как const_cast (), где модификатор const может быть удален, поэтому переменные const могут быть изменены и reinterpret_cast (), которые могут даже переинтерпретировать целочисленные значения в указатели.

вот пример.

int a=rand(); // Random number.

int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.

int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.

int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.

*pa4=5; // Program crashes.

основная причина, почему c++ бросает были добавлены к языку, чтобы позволить разработчику уточнить свои намерения-почему он собирается это сделать. Используя слепки в стиле C, которые являются абсолютно допустимыми в C++ , Вы делаете свой код менее читаемым и более подверженным ошибкам, особенно для других разработчиков, которые не создавали ваш код. Поэтому, чтобы сделать ваш код более читаемым и явным, вы всегда должны предпочесть приведения C++ над приведениями C-стиля.

вот короткая цитата из книги Бьярне Страуструпа (автора C++) C++ Язык программирования 4-е издание - стр. 302.

это приведение в стиле C намного опаснее, чем именованные операторы преобразования потому что нотацию сложнее обнаружить в большой программе, и вид преобразования, предназначенный программистом, не является явным.