Какое правильное использование:
static_cast
dynamic_cast
const_cast
reinterpret_cast
(type)value
type(value)
Как вы решаете, что использовать в каких случаях?
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.
Используйте dynamic_cast
для преобразования указателей/ссылок в иерархию наследования.
Используйте static_cast
для конверсий обычного типа.
Используйте reinterpret_cast
для низкоуровневого переинтерпретации битовых шаблонов. Используйте с особой осторожностью.
Используйте const_cast
для отбрасывания const/volatile
. Избегайте этого, если вы не застряли с использованием некорректного 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<char*>(&val)
?
Это может помочь, если вы знаете немного внутренних...
static_cast
static_cast
для них.A
в B
, static_cast
вызывает конструктор B
передавая A
качестве параметра. В качестве альтернативы, A
может иметь оператор преобразования (то есть A::operator B()
). Если B
не имеет такого конструктора или A
не имеет оператора преобразования, вы получите ошибку времени компиляции.A*
в B*
всегда успешно, если A и B находятся в иерархии наследования (или void), в противном случае вы получите ошибку компиляции.A&
B&
.dynamic_cast
(Base*)
до (Derived*)
может произойти сбой, если указатель фактически не является производным типом.A*
к B*
, если приведение неверно, то dynamic_cast вернет nullptr.A&
to B&
если приведение неверно, то dynamic_cast вызовет исключение bad_cast.const_cast
set<T>
который возвращает только свои элементы как const, чтобы убедиться, что вы не изменили его ключ. Однако, если ваше намерение состоит в том, чтобы изменить неключевые члены объекта, тогда все должно быть в порядке. Вы можете использовать const_cast для удаления константности.T& foo()
а также const T& foo()
. Чтобы избежать дублирования кода, вы можете применить const_cast для возврата значения одной функции из другой.reinterpret_cast
const_cast
неверен: только dynamic_cast
фактически имеет издержки времени выполнения. Похоже, что вы, возможно, изменили порядок и забыли переместить и перефразировать этот.
В дополнение к остальным ответам до сих пор здесь неочевидный пример, где 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);
Помогает ли это ответить на ваш вопрос?
Я никогда не использовал reinterpret_cast
, и задаюсь вопросом, работает ли он в случае, который ему нужен, это не запах плохого дизайна. В базе кода я работаю над dynamic_cast
. Разница с static_cast
заключается в том, что a dynamic_cast
выполняет проверку времени выполнения, которая может (безопаснее) или не может (больше накладных расходов) быть тем, что вы хотите (см. msdn).
В то время как другие ответы хорошо описывали все различия между C++ кастами, я хотел бы добавить короткую заметку, почему вы не должны использовать C-style cast (Type) var
и Type(var)
.
Для новичков C++ C-стиль выглядит как операция надмножества над C++ casts (static_cast <>(), dynamic_cast <>(), const_cast <>(), reinterpret_cast <>()), и кто-то может предпочесть их над C++. Фактически, C-стиль - это надмножество и короче, чтобы писать.
Основная проблема приведения в стиле C заключается в том, что они скрывают реальное намерение разработчика при создании. Стили C-стиля могут выполнять практически все типы кастинга из обычно безопасных отбросов, выполняемых static_cast <>() и dynamic_cast <>(), в потенциально опасные роли, такие как const_cast <>(), где модификатор 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-style casts, которые отлично подходят в C++, вы делаете свой код менее удобочитаемым и больше подвержены ошибкам, особенно для других разработчиков, которые не создали ваш код. Поэтому, чтобы сделать ваш код более читабельным и явным, вы всегда должны отдавать предпочтение C++ за кастинг над стилями C-стиля.
Вот короткая цитата из Bjarne Stroustrup (автора C++) книги C++ Язык программирования 4-го издания - стр. 302.
Это приведение в стиле C гораздо более опасно, чем именованные операторы преобразования, поскольку запись в сложной программе сложнее, а тип конверсии, предназначенной программисту, не является явным.
Чтобы понять, давайте рассмотрим ниже фрагмент кода:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
Только строка (4) компилируется без ошибок. Только reinterpret_cast может использоваться для преобразования указателя на объект в указатель на любой не связанный тип объекта.
Следует отметить следующее: dynamic_cast не будет работать во время выполнения, однако на большинстве компиляторов он также не будет компилироваться, поскольку в структуре преобразуемого указателя нет виртуальных функций, то есть dynamic_cast будет работать только с полиморфными указателями классов.,
Когда использовать C++ приведение: