c++ - разница - Регулярное литье против static_cast vs. dynamic_cast




static_cast dynamic_cast разница (6)

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 если вы опустили (cast to производный класс), а тип аргумента не является полиморфным. Например, следующий код недействителен, потому что Base не содержит никакой виртуальной функции:

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

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

Обычный

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

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

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

На этот вопрос уже есть ответ:

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

MyClass *m = (MyClass *)ptr;

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

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);

Статический литье

Статический кастинг выполняет преобразования между совместимыми типами. Он похож на 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 с использованием динамического переноса. Это преобразование с производной базой выполняется успешно, поскольку объект Child включает в себя полный базовый объект.

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. Поскольку объект Base не содержит полного объекта Child, это преобразование указателя не будет выполнено. Чтобы указать это, динамический кадр возвращает нулевой указатель. Это дает удобный способ проверить, удалось ли преобразование во время выполнения.

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);

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

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

// Incomplete MyChild object dereferenced
(*child);

Const cast

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

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

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

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

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

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

Затем функции можно передать постоянную переменную с помощью команды const.

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

print(nonConst); // allowed

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


FYI, я считаю, что Bjarne Stroustrup цитирует, что следует избегать приведения в стиле C и что вы должны использовать static_cast или dynamic_cast, если это вообще возможно.

Часто задаваемые вопросы по стилю C ++ Barne Stroustrup

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


Вы должны посмотреть на статью C ++ Programming / Type Casting .

Он содержит хорошее описание всех типов броска. Из приведенной выше ссылки:

const_cast

const_cast (выражение) const_cast <> () используется для добавления / удаления константы (или изменчивости) переменной.

static_cast

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

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

dynamic_cast

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

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

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

reinterpret_cast

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


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


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

C-style (и другие) отливки были освещены в других ответах.







casting