это - алена c++ const_cast




Когда следует использовать static_cast, dynamic_cast, const_cast и reinterpret_cast? (5)

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

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

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • (type)value cast (type)value стиле C
  • type(value)

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


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

Для начинающих C ++ стили C-стиля выглядят как операция надмножества над кастами C ++ (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-стиль, отличные от C ++, вы делаете свой код менее удобочитаемым и больше подвержены ошибкам, особенно для других разработчиков, которые не создали ваш код. Поэтому, чтобы сделать ваш код более читабельным и явным, вы всегда должны предпочитать, что C ++ выполняет броски C-стиля.

Вот короткая цитата из книги Bjarne Stroustrup (автор книги C ++). Язык программирования C ++ 4th edition - стр. 302.

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


Отвечает ли this на ваш вопрос?

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


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

static_cast

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

dynamic_cast

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

const_cast

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

reinterpret_cast

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

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

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

const_cast может использоваться для удаления или добавления const в переменную; никакие другие 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 и стилевая функция - это броски с использованием (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.







c++-faq