Regular cast vs. static_cast vs. dynamic_cast

c++ pointers casting

660456 просмотра

8 ответа

37473 Репутация автора

This question already has an answer here:

I've been writing C and C++ code for almost twenty years, but there's one aspect of these languages that I've never really understood. I've obviously used regular casts i.e.

MyClass *m = (MyClass *)ptr;

all over the place, but there seem to be two other types of casts, and I don't know the difference. What's the difference between the following lines of code?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
Автор: Graeme Perrow Источник Размещён: 26.08.2008 01:20

Ответы (8)


11 плюса

17280 Репутация автора

dynamic_castимеет проверку типов во время выполнения и работает только со ссылками и указателями, тогда static_castкак не предлагает проверку типов во время выполнения. Для получения полной информации см. Статью MSDN Оператор static_cast .

Автор: Inisheer Размещён: 26.08.2008 01:26

78 плюса

15450 Репутация автора

Вы должны взглянуть на статью C ++ Программирование / Приведение типов .

Он содержит хорошее описание всех различных типов актеров. Следующее взято из вышеуказанной ссылки:

const_cast

const_cast (expression) const_cast <> () используется для добавления / удаления const (ness) (или volatile-ness) переменной.

static_cast

static_cast (expression) static_cast <> () используется для приведения между целочисленными типами. например, char-> long, int-> short и т. д.

Статическое приведение также используется для приведения указателей к связанным типам, например приведение void * к соответствующему типу.

dynamic_cast

Динамическое приведение используется для преобразования указателей и ссылок во время выполнения, как правило, с целью преобразования указателя или ссылки вверх или вниз по цепочке наследования (иерархия наследования).

dynamic_cast (выражение)

Тип назначения должен быть указателем или ссылочным типом, а выражение должно указывать на указатель или ссылку. Динамическое приведение работает только тогда, когда тип объекта, на который ссылается выражение, совместим с целевым типом, а базовый класс имеет хотя бы одну виртуальную функцию-член. Если нет, и типом выражения, являющегося приведенным, является указатель, возвращается значение NULL, если при динамическом приведении ссылки происходит сбой, создается исключение bad_cast. Когда это не дает сбоя, динамическое приведение возвращает указатель или ссылку целевого типа на объект, на который ссылается выражение.

reinterpret_cast

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

Автор: TJ Seabrooks Размещён: 26.08.2008 01:28

15 плюса

15337 Репутация автора

Приведения в стиле C объединяют const_cast, static_cast и reinterpret_cast.

Я бы хотел, чтобы в C ++ не было приведений в стиле C. Приведения типов в C ++ выделяются должным образом (как и должно быть; приведения типов обычно указывают на то, что они делают что-то плохое) и должным образом различают различные виды преобразования, выполняемые приведениями. Они также позволяют писать похожие функции, например boost :: lexical_cast, что довольно неплохо с точки зрения согласованности.

Автор: DrPizza Размещён: 26.08.2008 01:38

27 плюса

112300 Репутация автора

К вашему сведению, я считаю, что Бьярн Страуструп говорит, что следует избегать приведений в стиле C и что вы должны использовать static_cast или dynamic_cast, если это вообще возможно.

Часто задаваемые вопросы о стиле C ++ Барна Страуструпа

Прими этот совет за то, что ты будешь. Я далеко не гуру C ++.

Автор: Jason Baker Размещён: 26.08.2008 01:39

27 плюса

2241 Репутация автора

Избегайте использования бросков C-Style.

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

Автор: ugasoft Размещён: 19.09.2008 05:30

1523 плюса

420304 Репутация автора

Решение

static_cast

static_castиспользуется в тех случаях, когда вы в основном хотите отменить неявное преобразование с некоторыми ограничениями и дополнениями. static_castне выполняет проверки во время выполнения. Это следует использовать, если вы знаете, что ссылаетесь на объект определенного типа, и, следовательно, проверка не требуется. Пример:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

В этом примере вы знаете, что передали MyClassобъект, и, следовательно, нет необходимости в проверке во время выполнения, чтобы это гарантировать.

dynamic_cast

dynamic_castполезно, когда вы не знаете, что такое динамический тип объекта. Он возвращает нулевой указатель, если указанный объект не содержит тип, приведенный к базовому классу (когда вы приводите ссылку, bad_castв этом случае выдается исключение).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

Вы не можете использовать, dynamic_castесли вы понижены (приведен к производному классу) и тип аргумента не является полиморфным. Например, следующий код недопустим, потому Baseчто не содержит никакой виртуальной функции:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

«Повышение» (приведение к базовому классу) всегда допустимо для обоих static_castи dynamic_cast, а также без каких-либо приведений, поскольку «приведение» является неявным преобразованием.

Обычный актерский состав

Эти броски также называются бросками в стиле C. Приведение в стиле C в основном идентично испытанию ряда последовательностей приведения C ++ и принятию первого приведения C ++, который работает, даже не задумываясь dynamic_cast. Излишне говорить, что это гораздо более мощный, поскольку он объединяет все const_cast, static_castи reinterpret_cast, но это также небезопасно, потому что он не использует dynamic_cast.

Кроме того, приведения в стиле C не только позволяют вам делать это, но также позволяют безопасно приводить к частному базовому классу, в то время как «эквивалентная» static_castпоследовательность выдаст вам ошибку времени компиляции.

Некоторые люди предпочитают броски в стиле C из-за их краткости. Я использую их только для числовых приведений и использую соответствующие преобразования C ++, когда задействованы определенные пользователем типы, поскольку они обеспечивают более строгую проверку.

Автор: Johannes Schaub - litb Размещён: 10.08.2009 01:50

11 плюса

9421 Репутация автора

dynamic_castподдерживает только указатель и ссылочные типы. Он возвращает, NULLесли приведение невозможно, если тип является указателем, или выдает исключение, если тип является ссылочным типом. Следовательно, dynamic_castможет использоваться, чтобы проверить, является ли объект данного типа, static_castне может (вы просто получите недопустимое значение).

С-образные (и другие) приведения были рассмотрены в других ответах.

Автор: larsmoa Размещён: 05.02.2012 05:10

158 плюса

9199 Репутация автора

Статическое приведение

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

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Поскольку это приводит к тому, что 4-байтовый указатель указывает на 1 байт выделенной памяти, запись в этот указатель либо вызовет ошибку времени выполнения, либо перезапишет некоторую смежную память.

*p = 5; // run-time error: stack corruption

В отличие от преобразования типа C, статическое приведение позволит компилятору проверить совместимость типов данных указателя и указателя, что позволяет программисту уловить это неправильное назначение указателя во время компиляции.

int *q = static_cast<int*>(&c); // compile-time error

Переосмыслить приведение

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

int *r = reinterpret_cast<int*>(&c); // forced conversion

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

Динамический состав

Этот используется только для преобразования указателей объектов и ссылок на объекты в другие указатели или ссылочные типы в иерархии наследования. Это единственное приведение, которое гарантирует, что указанный объект может быть преобразован путем выполнения проверки во время выполнения, что указатель ссылается на законченный объект целевого типа. Чтобы эта проверка во время выполнения была возможной, объект должен быть полиморфным. То есть класс должен определять или наследовать хотя бы одну виртуальную функцию. Это потому, что компилятор будет генерировать только необходимую информацию о типе времени выполнения для таких объектов.

Примеры динамического приведения

В приведенном ниже примере указатель MyChild преобразуется в указатель MyBase с использованием динамического приведения. Это преобразование из производной в базовую успешно выполнено, поскольку дочерний объект включает в себя полный базовый объект.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

В следующем примере делается попытка преобразовать указатель MyBase в указатель MyChild. Поскольку базовый объект не содержит завершенного дочернего объекта, преобразование указателя завершится неудачно. Чтобы указать это, динамическое приведение возвращает нулевой указатель. Это дает удобный способ проверить, успешно ли выполнено преобразование во время выполнения.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

Если вместо указателя преобразуется ссылка, динамическое приведение завершится неудачей, вызвав исключение bad_cast. Это должно быть обработано с помощью оператора try-catch.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

Динамическое или статическое приведение

Преимущество использования динамического приведения заключается в том, что он позволяет программисту проверять, успешно ли выполнено преобразование во время выполнения. Недостатком является то, что при выполнении этой проверки возникают накладные расходы. По этой причине использование статического приведения было бы предпочтительнее в первом примере, потому что преобразование из производного в базовое никогда не завершится неудачей.

MyBase *base = static_cast<MyBase*>(child); // ok

Однако во втором примере преобразование может быть либо успешным, либо неудачным. Он потерпит неудачу, если объект MyBase содержит экземпляр MyBase, и будет успешным, если он содержит экземпляр MyChild. В некоторых ситуациях это может быть неизвестно до времени выполнения. В этом случае динамическое приведение является лучшим выбором, чем статическое приведение.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

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

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Const Cast

Этот в основном используется для добавления или удаления модификатора const переменной.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Хотя метод const позволяет изменять значение константы, это все равно является недействительным кодом, который может вызвать ошибку во время выполнения. Это может произойти, например, если константа находится в разделе только для чтения.

*nonConst = 10; // potential run-time error

Вместо этого используется константное приведение, главным образом, когда есть функция, которая принимает аргумент неконстантного указателя, даже если она не изменяет объект-указатель.

void print(int *p) 
{
   std::cout << *p;
}

Затем функции можно передать постоянную переменную, используя константное приведение.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

Источник и дополнительные пояснения

Автор: Breeze Размещён: 24.08.2013 01:55
32x32