Запутался в приведении C++

Я много читал о приведении C++ и начинаю запутываться, потому что всегда использовал приведение в стиле C.

Я читал, что приведения в стиле C следует избегать в C++ и что reinterpret_cast очень и очень опасен и не должен использоваться всякий раз, когда есть альтернатива. В отличие от того, чтобы не использовать reinterpret_cast, я много раз видел, как он использовался в MSDN в их примере кода. Это заставляет меня задать свой первый вопрос: когда можно использовать reinterpret_cast?

Например:

LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
   switch (Msg)
   {
      case WM_CREATE:
      {
          LPCREATESTRUCT lpCreateStruct = reinterpret_cast<LPCREATESTRUCT>(lParam); 
          return 0;
      }
   }

   ...
}

Если это не нормально, то как мне привести значение LPARAM к указателю, используя только статическое, динамическое и/или константное приведение?

Также: если reinterpret_cast не является переносимым, как мне переписать его, чтобы он был переносимым (для хорошей практики)


person Marlon    schedule 27.02.2010    source источник
comment
Это может быть ОЧЕНЬ старая кодовая база.   -  person Hamish Grubijan    schedule 27.02.2010


Ответы (5)


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

Он считается опасным, поскольку не выполняет проверки ни во время компиляции, ни во время выполнения. Если вы сделаете ошибку, она может и будет давать сбой и ужасно гореть, и ее будет трудно отлаживать. По сути, вы говорите компилятору: «Я лучше вас знаю, что это такое на самом деле, поэтому просто скомпилируйте код и позвольте мне беспокоиться о последствиях».

person MikeP    schedule 27.02.2010
comment
Сериализация структур таким образом - плохая идея (даже если это делается очень часто), но это тема для другого вопроса. - person Tronic; 27.02.2010

Причина, по которой вы видите его в MSDN, заключается в том, что Win32 API — это API C, но люди настаивают на том, чтобы приводить примеры на C++.

Reinterpret cast подходит, когда вы пишете код, взаимодействующий с другими библиотеками. Его следует избегать в вашем собственном приложении.

person Frank Krueger    schedule 27.02.2010

Это пример программирования Windows Platform SDK, C API, с помощью C++. Оконная процедура имеет только параметры WPARAM и LPARAM, и если вам нужно передать указатель на структуру через оконное сообщение, его нужно привести. На мой взгляд, это вполне приемлемое использование reinterpret_cast‹>. Вы не можете избежать приведения, потому что SDK, в который вы пишете, который не является вашим кодом, не был разработан для C++, не говоря уже о безопасности типов, и требует приведения для предоставления универсальных типов параметров с привязкой C.

Здесь reinterpret_cast‹> — это флаг, который сообщает вам, что вам нужно быть осторожным, но его нельзя избегать любой ценой.

Если, с другой стороны, вы контролируете обе стороны кода, API и потребителя, было бы лучше сделать API, который был бы типобезопасным и не требовал бы, чтобы потребитель выполнял приведения типов для его правильного использования.

person GBegen    schedule 27.02.2010

Не хочу не уважать MSDN, но MSDN — не лучшее место для правильного кодирования на C++.

Одна из причин использования reinterpret_cast — это когда вы выполняете приведение к непрозрачным типам данных или из них. reinterpret_cast не является «опасным», просто его легко испортить и привести к проблемам в вашем коде, поэтому его следует избегать.

Причина, по которой предпочтительны приведения в стиле C++, заключается в том, что static_cast является типобезопасным, и все времена приведения легче искать.

Программисты [неправильно] часто используют приведения для «отбрасывания предупреждений компилятора», таких как преобразование из целых чисел без знака в целые числа со знаком или из 32-битного целого числа в 8-битное.

person Alan    schedule 27.02.2010
comment
+1. MSDN битком набит MS-измами. Хорошие книги Скотта Мейерса и Херба Саттера (хотя сейчас он учится в магистратуре, он отличный писатель и дает много отличных советов по написанию хорошего C++) лучше всего подходят для того, чтобы закрепить понимание хорошей практики C++. - person Matt Curtis; 27.02.2010
comment
@ Мэтт Кертис - меня больше всего раздражает в MSDN то, что MS-измы применяются непоследовательно. Качество сильно различается в зависимости от их примеров. Однако, вообще говоря, вам не следует копировать их примеры; это может дать вам приблизительное представление, но вы должны адаптировать эти идеи к любому стилю кодирования, используемому в проекте, над которым вы работаете. - person asveikau; 27.02.2010

По сути, reinterpret_cast «безопасен» со структурами C и базовыми типами (за исключением простых ошибок, таких как приведение int к указателю и обратно, что работает на архитектуре ILP32, но ломается на LP64). В структуре C нет ничего, кроме возможного заполнения. для выравнивания, которое вы не заявляли.

reinterpret_cast небезопасно с полиморфными типами C++, поскольку компилятор вставляет элементы данных в ваш класс — например, указатели на виртуальные таблицы и указатели на виртуальные базовые классы< /эм>. Другие приведения C++ заботятся об их настройке, когда, скажем, приведение вниз от указателя на базовый класс к указателю на производный класс, reinterpret_cast и приведения в стиле C не делают этого.

person Nikolai Fetissov    schedule 27.02.2010