когда следует использовать static\_cast, dynamic\_cast, const\_cast и reinterpret\_cast?

<p>как правильно использовать:</p><ul> <li><code>static\_cast</code></li> <li><code>dynamic\_cast</code></li> <li><code>const\_cast</code></li> <li><code>reinterpret\_cast</code></li> <li>приведение в стиле c <code>(type)value</code></li> <li>приведение в стиле функции <code>type(value)</code></li> </ul><p>как решить, что использовать в каких конкретных случаях?</p>

<p><strong><code>static\_cast</code></strong>это первый каст, который вы должны попытаться использовать. он выполняет такие вещи, как неявные преобразования между типами (например, <code>int</code> в <code>float</code> или указатель на <code>void*</code>), и может также вызывать функции явного преобразования (или неявные). во многих случаях явное указание <code>static\_cast</code> не является необходимым, но важно отметить, что синтаксис <code>t(something)</code> эквивалентен <code>(t)something</code> и его следует избегать (подробнее об этом позже). однако функция <code>t(something, something\_else)</code> безопасна и может вызывать конструктор.</p><p><code>static\_cast</code> также может приводиться через иерархии наследования. это не нужно при приведении вверх (к базовому классу), но при приведении вниз его можно использовать до тех пор, пока оно не преобразуется через наследование <code>virtual</code>. однако он не выполняет проверку и не определяет поведение <code>static\_cast</code> вниз по иерархии до типа, который на самом деле не является типом объекта.</p><hr><p><strong><code>const\_cast</code></strong>может использоваться для удаления или добавления <code>const</code> к переменной; никакой другой c++ cast не способен удалить его (даже <code>reinterpret\_cast</code>). важно отметить, что изменение прежнего значения <code>const</code> не определено, только если исходной переменной является <code>const</code>; если вы используете его, чтобы убрать <code>const</code> со ссылки на что-то, что не было объявлено с <code>const</code>, это безопасно. это может быть полезно при перегрузке функций-членов, например, на основе <code>const</code>. его также можно использовать для добавления <code>const</code> к объекту, например, для вызова перегрузки функции-члена.</p><p><code>const\_cast</code> также работает аналогично с <code>volatile</code>, но это встречается реже.</p><hr><p><strong><code>dynamic\_cast</code></strong>используется исключительно для обработки полиморфизма. вы можете привести указатель или ссылку на любой полиморфный тип к любому другому типу класса (полиморфный тип имеет как минимум одну виртуальную функцию, объявленную или унаследованную). вы можете использовать его не только для того, чтобы бросать вниз - вы можете кастовать вбок или даже на другую цепь. <code>dynamic\_cast</code> найдет нужный объект и вернет его, если это возможно. если он не может, он вернет <code>nullptr</code> в случае указателя или выбросит <code>std::bad\_cast</code> в случае ссылки.</p><p>однако <code>dynamic\_cast</code> имеет некоторые ограничения. он не работает, если в иерархии наследования есть несколько объектов одного типа (так называемый «страшный бриллиант»), и вы не используете наследование <code>virtual</code>. он также может проходить только через публичное наследование - он всегда не сможет пройти через наследование <code>protected</code> или <code>private</code>. однако это редко является проблемой, поскольку такие формы наследования встречаются редко.</p><hr><p><strong><code>reinterpret\_cast</code></strong>является наиболее опасным приведением, и его следует использовать очень экономно. он превращает один тип непосредственно в другой - например, приведение значения от одного указателя к другому или сохранение указателя в <code>int</code>, или всякие другие неприятные вещи. в основном, единственная гарантия, которую вы получаете с <code>reinterpret\_cast</code>, заключается в том, что обычно, если вы приведете результат обратно к исходному типу, вы получите точно такое же значение (но<strong> <em>not</em> </strong>, если промежуточный тип меньше исходного тип). есть ряд преобразований, которые<strong><code>reinterpret\_cast</code></strong>тоже не могут сделать. он используется в основном для особенно странных преобразований и битовых манипуляций, таких как превращение потока необработанных данных в реальные данные или хранение данных в младших битах выровненного указателя.</p><hr><p><strong> приведение в стиле c </strong>и<strong> приведение в стиле функции </strong>- приведение с использованием <code>(type)object</code> или <code>type(object)</code> соответственно и функционально эквивалентны. они определены как первое из следующего, что успешно:</p><ul> <li><code>const\_cast</code></li> <li><code>static\_cast</code> (хотя игнорируя ограничения доступа)</li> <li><code>static\_cast</code> (см. выше), затем <code>const\_cast</code></li> <li><code>reinterpret\_cast</code></li> <li><code>reinterpret\_cast</code>, затем <code>const\_cast</code></li> </ul><p>поэтому в некоторых случаях он может использоваться в качестве замены для других приведений, но может быть чрезвычайно опасным из-за способности переходить в <code>reinterpret\_cast</code>, и последний должен быть предпочтительным, когда требуется явное приведение, если вы не уверены, что <code>static\_cast</code> будет успешным или <code>reinterpret\_cast</code> потерпит неудачу. даже тогда рассмотрим более длинный и более явный вариант.</p><p>приведения типа c также игнорируют управление доступом при выполнении <code>static\_cast</code>, что означает, что они имеют возможность выполнять операции, которые не могут выполнять другие приведения. хотя это в основном клудж, и, на мой взгляд, это просто еще одна причина избегать бросков в стиле c.</p>

<p>используйте <code>dynamic\_cast</code> для преобразования указателей/ссылок в иерархии наследования.</p><p>используйте <code>static\_cast</code> для обычных преобразований типов.</p><p>используйте <code>reinterpret\_cast</code> для низкоуровневой реинтерпретации битовых комбинаций. используйте с особой осторожностью.</p><p>используйте <code>const\_cast</code> для отбрасывания <code>const/volatile</code>. избегайте этого, если только вы не застряли с использованием некорректного api-интерфейса.</p>

<p><em>(много теоретического и концептуального объяснения было дано выше)</em> </p><p>ниже приведены некоторые из <strong>практических примеров</strong>, когда я использовал <strong>static\_cast</strong>, <strong>dynamic\_cast</strong>, <strong>const\_cast</strong>, <strong>reinterpret\_cast</strong>.</p><p>(также ссылается на это, чтобы понять объяснение: <a href="http://www.cplusplus.com/doc/tutorial/typecasting/">http://www.cplusplus.com/doc/tutorial/typecasting/</a> )</p><p><strong>static\_cast:</strong></p><pre><code>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); ..... } </code></pre><p><strong>dynamic\_cast:</strong></p><pre><code>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( ... )*/{ // ... } } </code></pre><p><strong>const\_cast:</strong></p><pre><code>// *passwd declared as a const const unsigned char *passwd // on some situation it require to remove its constness const\_cast<unsigned char*>(passwd) </code></pre><p><strong>reinterpret\_cast:</strong></p><pre><code>typedef unsigned short uint16; // read bytes returns that 2 bytes got read. bool bytebuffer::readuint16(uint16& val) { return readbytes(reinterpret\_cast<char*>(&val), 2); } </code></pre>

<p>это может помочь, если вы знаете немного внутренних ...</p><p><strong> static\_cast </strong></p><ul> <li>компилятор c++ уже знает, как преобразовывать типы масштабирования, такие как float, в int. используйте <code>static\_cast</code> для них.</li> <li>когда вы просите компилятор преобразовать тип <code>a</code> в <code>b</code>, <code>static\_cast</code> вызывает конструктор <code>b</code>, передавая <code>a</code> в качестве параметра. в качестве альтернативы, <code>a</code> может иметь оператор преобразования (т.е. <code>a::operator b()</code>). если у <code>b</code> такого конструктора нет или у <code>a</code> нет оператора преобразования, вы получите ошибку времени компиляции. </li> <li>преобразование из <code>a*</code> в <code>b*</code> всегда завершается успешно, если a и b находятся в иерархии наследования (или void), в противном случае вы получаете ошибку компиляции.</li> <li><strong> понятно </strong>: если вы приведете базовый указатель к производному указателю, но если фактический объект на самом деле не является производным типом, то вы <em>not</em> получите ошибку. вы получаете плохой указатель и, скорее всего, segfault во время выполнения. то же самое касается <code>a&</code> до <code>b&</code>.</li> <li><strong> поправляю </strong>: приведение из derived в base или наоборот создает <em>new</em> копию! для людей, пришедших из c #/java, это может быть огромным сюрпризом, поскольку в результате получается в основном отрубленный объект, созданный из derived.</li> </ul><p><strong> dynamic\_cast </strong></p><ul> <li>dynamic\_cast использует информацию о типе среды выполнения, чтобы выяснить, является ли приведение действительным. например, от <code>(base*)</code> до <code>(derived*)</code> может произойти сбой, если указатель фактически не является производным типом.</li> <li>это означает, что dynamic\_cast очень дорогой по сравнению со static\_cast!</li> <li>от <code>a*</code> до <code>b*</code>, если приведение неверно, dynamic\_cast вернет nullptr.</li> <li>если от <code>a&</code> до <code>b&</code>, если приведение неверно, то dynamic\_cast вызовет исключение bad\_cast.</li> <li>в отличие от других приведений, есть накладные расходы времени выполнения.</li> </ul><p><strong> const\_cast </strong></p><ul> <li>в то время как static\_cast может делать неконстантный констант, он не может идти другим путем. const\_cast может работать в обоих направлениях.</li> <li>одним из примеров, где это удобно, является перебор некоторого контейнера, такого как <code>set<t></code>, который возвращает только его элементы как const, чтобы убедиться, что вы не изменили его ключ. однако если ваше намерение состоит в том, чтобы изменить неключевые члены объекта, тогда все должно быть в порядке. вы можете использовать const\_cast для удаления константности.</li> <li>другой пример - когда вы хотите реализовать <code>t& foo()</code>, а также <code>const t& foo()</code>. чтобы избежать дублирования кода, вы можете применить const\_cast для возврата значения одной функции из другой.</li> </ul><p><strong> reinterpret\_cast </strong></p><ul> <li>это в основном говорит о том, что возьмите эти байты в этой ячейке памяти и воспринимайте это как заданный объект.</li> <li>например, вы можете загрузить 4 байта с плавающей точкой до 4 байтов с целым числом, чтобы увидеть, как выглядят биты в плавающей точке.</li> <li>очевидно, что если данные не соответствуют типу, вы можете получить segfault.</li> <li>для этого состава нет накладных расходов времени выполнения.</li> </ul>

<p> <a href="http://www.cppreference.com/wiki/keywords/casting\_comparison">это</a> отвечает на ваш вопрос?</p><p>я никогда не использовал <code>reinterpret\_cast</code>, и я удивляюсь, не пахнет ли случай, который нуждается в этом, плохим дизайном. в базе кода я работаю на <code>dynamic\_cast</code> используется много. разница с <code>static\_cast</code> состоит в том, что <code>dynamic\_cast</code> выполняет проверку во время выполнения, которая может (безопаснее) или не может (больше накладных расходов) быть тем, что вы хотите (см. <a href="http://msdn.microsoft.com/en-us/library/c36yw7x9(vs.80).aspx">msdn</a> ).</p>

<p>в дополнение к другим ответам, приведенным выше, приведен неочевидный пример, когда <code>static\_cast</code> недостаточно, чтобы <code>reinterpret\_cast</code> был необходим. предположим, есть функция, которая в выходном параметре возвращает указатели на объекты разных классов (которые не разделяют общий базовый класс). настоящим примером такой функции является <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615%28v=vs.85%29.aspx"><code>cocreateinstance()</code></a> (см. последний параметр, который фактически является <code>void**</code>). предположим, вы запрашиваете определенный класс объекта у этой функции, поэтому заранее знаете тип указателя (что вы часто делаете для com-объектов). в этом случае вы не можете привести указатель к вашему указателю в <code>void**</code> с помощью <code>static\_cast</code>: вам нужна <code>reinterpret\_cast<void**>(&yourpointer)</code>. </p><p>в коде:</p><pre><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) ); </code></pre><p>однако <code>static\_cast</code> работает для простых указателей (не указателей на указатели), поэтому приведенный выше код можно переписать, чтобы избежать <code>reinterpret\_cast</code> (по цене дополнительной переменной) следующим образом:</p><pre><code>#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); </code></pre>

<p>в то время как другие ответы хорошо описывают все различия между приведениями в c++, я хотел бы добавить краткое замечание, почему вы не должны использовать приведение в стиле c <code>(type) var</code> и <code>type(var)</code>.</p><p>для начинающих в c++ приведение в стиле c выглядит как операция надмножества приведения в c++ (static\_cast <> (), dynamic\_cast <> (), const\_cast <> (), reinterpret\_cast <> ()), и кто-то может предпочесть их приведениям c++ , на самом деле, c-стиль - это суперсет, который короче для написания.</p><p>основная проблема приведений в стиле c заключается в том, что они скрывают реальные намерения разработчиков. приведения в стиле c могут выполнять практически все типы приведения, от обычно безопасных приведения, выполняемых static\_cast <> () и dynamic\_cast <> (), к потенциально опасным приведениям, таким как const\_cast <> (), где модификатор const можно удалить, поэтому переменные const может быть изменен и reinterpret\_cast <> (), который может даже интерпретировать целочисленные значения для указателей.</p><p>вот образец.</p><pre><code>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. </code></pre><p>основная причина, по которой в язык были добавлены приведения c++, заключалась в том, чтобы позволить разработчику уточнить свои намерения - почему он собирается делать это приведение. используя приведения в стиле c, которые совершенно допустимы в c++, вы делаете свой код менее читабельным и более подверженным ошибкам, особенно для других разработчиков, которые не создавали ваш код. поэтому, чтобы сделать ваш код более читабельным и явным, вы всегда должны отдавать предпочтение приведению в c++, а не в стиле c.</p><p>вот краткая цитата из книги бьярна страуструпа (автора c++) «язык программирования c++, 4-е издание» - стр. 302.</p><blockquote> <p>это приведение в стиле c гораздо более опасно, чем именованные операторы преобразования потому что обозначения труднее обнаружить в большой программе, и вид преобразования, предназначенный программистом, не является явным.</p> </blockquote>

<p>чтобы понять, давайте рассмотрим ниже фрагмент кода:</p><pre><code>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; } </code></pre><p>только строка (4) компилируется без ошибок. только<strong> reinterpret\_cast </strong>может использоваться для преобразования указателя на объект в указатель на любой не связанный тип объекта.</p><p>следует отметить следующее:<strong> dynamic\_cast </strong>потерпит неудачу во время выполнения, однако на большинстве компиляторов также не удастся скомпилировать, поскольку в структуре преобразуемого указателя нет виртуальных функций, то есть<strong> dynamic\_cast </strong>будет работать только с полиморфными указателями классов.</p><p><strong> когда использовать c++ cast </strong>:</p><ul> <li>используйте<strong> static\_cast </strong>в качестве эквивалента приведения типа c, который выполняет преобразование значения, или когда нам нужно явно преобразовать указатель из класса в его суперкласс.</li> <li>используйте<strong> const\_cast </strong>, чтобы удалить квалификатор const. </li> <li>используйте<strong> reinterpret\_cast </strong>для небезопасных преобразований типов указателей в целочисленные и другие типы указателей и из них. используйте это, только если мы знаем, что делаем, и понимаем проблемы псевдонимов.</li> </ul>