Регулярное приведение против статического приведения против динамического приведения [дубликат]
этот вопрос уже есть ответ здесь:
Я пишу код на C и C++ уже почти двадцать лет, но есть один аспект этих языков, который я никогда не понимал. Я, очевидно, использовал регулярные слепки то есть
MyClass *m = (MyClass *)ptr;
повсюду, но, похоже, есть два других типа бросков, и я не знаю разницы. В чем разница между следующими строками кода?
MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
8 ответов:
метод 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полезно, когда вы не знаете, что динамический тип объекта. Он возвращает нулевой указатель, если объект, на который ссылается, не содержит тип, приведенный к базовому классу (при приведении к ссылке abad_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 }"up-cast" (приведение к базовому классу) всегда действителен с обоими
static_castиdynamic_cast, а также без какого-либо приведения, поскольку "up-cast" является неявным преобразованием.Обычный Литой
эти броски также называются C-style cast. Приведение в стиле C в основном идентично опробованию ряда последовательностей приведений C++ и взятию первого приведения C++, которое работает, никогда не рассматривая
dynamic_cast. Излишне говорить, что это гораздо более мощный, как он сочетает в себе всеconst_cast,static_castиreinterpret_cast, но это также небезопасно, потому что это не использоватьdynamic_cast.кроме того, приведения в стиле C не только позволяют это сделать, но и позволяют безопасно привести к частному базовому классу, в то время как "эквивалент"
static_castпоследовательность даст вам ошибку времени компиляции для этого.некоторые люди предпочитают C-образные слепки из-за их краткости. Я использую их только для числовых бросает , и используйте соответствующие приведения C++, когда задействованы определенные пользователем типы, поскольку они обеспечивают более строгую проверку.
статический литой
статическое приведение выполняет преобразования между совместимыми типами. Он похож на литье в стиле C, но является более ограничительным. Например, приведение в стиле C позволит целочисленному указателю указывать на символ.
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytesпоскольку это приводит к 4-байтному указателю, указывающему на 1 байт выделенной памяти, запись в этот указатель либо вызовет ошибку времени выполнения, либо перезапишет некоторую соседнюю память.
*p = 5; // run-time error: stack corruptionв отличие от приведение в стиле C, статическое приведение позволит компилятору проверить совместимость типов данных pointer и pointee, что позволяет программисту поймать это неправильное назначение указателя во время компиляции.
int *q = static_cast<int*>(&c); // compile-time errorReinterpret cast
для принудительного преобразования указателя, так же, как и приведение 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 cast позволяет изменять значение константы, это все еще недопустимый код, который может вызвать ошибку во время выполнения. Это может произойти, например, если константа находится в разделе только для чтения памяти.
*nonConst = 10; // potential run-time errorConst cast вместо этого используется в основном, когда есть функция, которая принимает непостоянный аргумент указателя, даже если он не изменяет ссылающиеся на заданный.
void print(int *p) { std::cout << *p; }затем функция может быть передана постоянной переменной с помощью приведения const.
print(&myConst); // error: cannot convert // const int* to int* print(nonConst); // allowed
вы должны посмотреть в статье Программирование На C++ / Приведение Типов.
содержит подробное описание всех различных видах. Следующее взято из приведенной выше ссылки:
const_cast
const_cast (выражение) const_cast () используется для добавления / удаления const (ness) (или volatile-ness) переменной.
метод static_cast
static_cast (выражение) The static_cast() используется для приведения между целочисленный тип. 'например' Чара->длинный, инт-короче и т. д.
статическое приведение также используется для приведения указателей к связанным типам, например пример приведения void* к соответствующему типу.
операцию dynamic_cast
динамическое приведение используется для преобразования указателей и ссылок во время выполнения, обычно для приведения указателя или ссылки вверх или вниз цепочка наследования (иерархия наследования).
динамическое приведение dynamic_cast(выражение)
целевой тип должен быть указателем или ссылочным типом, а выражение должно вычисляться как указатель или ссылка. Динамические литые заготовки только если тип объекта, на который ссылается выражение совместимость с целевым типом и базовым классом имеет по крайней мере один виртуальная функция-член. Если нет, то и тип выражаемого выражения является указателем, NULL возвращается, если динамическое приведение по ссылке не исключение bad_cast это заброшенный. Когда он не терпит неудачу, динамический cast возвращает указатель или ссылку целевого типа на объект к какому выражению относится.
оператора reinterpret_cast
Reinterpret cast просто приводит один тип побитово к другому. Любой указатель или целочисленный тип может быть преобразован в любой другой переиначить литой, легко позволяя для неправильного использования. Например, с переинтерпретацией литой один может, небезопасно, привести целочисленный указатель к указателю строки.
Не использовать C-стиля бросает.
C-style casts-это смесь const и reinterpret cast, и ее трудно найти и заменить в вашем коде. Программист на C++ приложения должны избегать Си-стиль приведения типов.
к вашему сведению, я считаю, что Бьярне Страуструп говорит, что приведений в стиле C следует избегать и что вы должны использовать static_cast или dynamic_cast, если это вообще возможно.
Барн Страуструп в стиле C++ FAQ
принимать этот совет за то, что вы будете. Я далек от того, чтобы быть гуру C++.
C-стиль приводит к слиянию const_cast, static_cast и reinterpret_cast.
Я бы хотел, чтобы у C++ не было C-стиля. Приведения C++ выделяются правильно (как и должны; приведения обычно указывают на то, что они делают что-то плохое) и правильно различают различные виды преобразования, которые выполняют приведения. Они также позволяют писать похожие функции, например boost:: lexical_cast, что довольно приятно с точки зрения согласованности.
dynamic_castимеет проверку типа времени выполнения и работает только со ссылками и указателями, тогда какstatic_castне предлагает проверку типа среды выполнения. Для получения полной информации см. статью MSDN оператор static_cast.
dynamic_castподдерживает только указатели и ссылочные типы. Он возвращаетNULLесли приведение невозможно, если тип является указателем или бросает исключение, если тип является ссылочным типом. Следовательно,dynamic_castможет быть использован для проверки, если объект данного типа,static_castне может (вы просто получите недопустимое значение).C-стиль (и другие) приведения были рассмотрены в других ответах.
Comments