7 ответов:
static_castэто первый бросок, который вы должны попытаться использовать. Он делает такие вещи, как неявные преобразования между типами (например,inttofloat, или указатель наvoid*), и он также может вызывать явные функции преобразования (или неявные). Во многих случаях, явно заявляяstatic_castне обязательно, но важно отметить, чтоT(something)синтаксис эквивалентно(T)somethingи этого следует избегать (об этом позже). АT(something, something_else)безопасно, однако, и гарантированно позвонить конструктор.
static_castможно также привести через иерархии наследования. Это не нужно при литье вверх (в сторону базового класса), но при литье вниз он может быть использован до тех пор, пока он не бросает черезvirtualнаследование. Однако он не выполняет проверку, и это неопределенное поведение дляstatic_castвниз по иерархии до типа, который на самом деле не является типом объекта.
const_castможно использовать для удаления или добавленияconstto переменная; никакое другое приведение 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-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для них.- в общем случае преобразования из типа
AtoB,static_castзвонкиBконструктор проходитAпарам. ЕслиBнет такого конструктора, то вы получите ошибку времени компиляции.- Cast from
A*доB*всегда успешно, если A и B находятся в иерархии наследования (или void) в противном случае вы получаете ошибку компиляции.- понял: если вы приведете базовый указатель к производному указателю, но если фактический объект на самом деле не является производным типом, то вы не получаю ошибку. Вы получаете плохой указатель и segfault во время выполнения. То же самое касается
A&доB&.- понял: приведение от производного к основанию или наоборот создает новая понял! Для людей, пришедших из 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_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++ casts, я хотел бы добавить короткую заметку, почему вы не должны использовать C-style casts
(Type) varиType(var).для новичков C++ приведения в стиле C выглядят как операция над набором над приведениями C++ (static_cast (), dynamic_cast (), const_cast (), reinterpret_cast ()) и кто-то может предпочесть их над приведениями C++. На самом деле c-стиль cast является надмножеством и короче писать.
главная проблема C-стиль отливки заключается в том, что они скрывают реальное намерение разработчика броска. Приведения в стиле 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 намного опаснее, чем именованные операторы преобразования потому что обозначение труднее обнаружить в большой программе, и вид преобразования, предназначенный программистом, не является явным.
Comments