Обычный литой и статическое литье и динамическое приведение [дубликат]

этот вопрос уже есть ответ здесь:

Я пишу код на 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 object, и, таким образом, нет необходимости в проверке выполнения для обеспечения этот.

операцию 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
}

" 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-style cast, но является более ограничительным. Например, приведение в стиле 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

Reinterpret cast

чтобы принудительно преобразовать указатель, так же, как это делает C-style cast в фоновом режиме, вместо этого будет использоваться reinterpret cast.

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 error

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

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

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

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

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

операцию dynamic_cast

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

динамическое приведение dynamic_cast(выражение)

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

оператора reinterpret_cast

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


Не использовать C-стиля бросает.

C-style casts-это смесь const и reinterpret cast, и в вашем коде трудно найти и заменить. Программист приложений на C++ должен избегать приведения в C-стиле.


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

Барн Страуструп в стиле C++ FAQ

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


C-style бросает conflate 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-style (и другие) броски были охвачены в других ответах.