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

StackOverflow https://stackoverflow.com/questions/28002

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

MyClass *m = (MyClass *)ptr;

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

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
Это было полезно?

Решение

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

динамический_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++, когда задействованы пользовательские типы, поскольку они обеспечивают более строгую проверку.

Другие советы

Статический актерский состав

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

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 переменной.

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

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

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

print(nonConst); // allowed

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

Вам стоит посмотреть статью Программирование на C++/приведение типов.

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

const_cast

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

static_cast

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

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

динамический_cast

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

динамический_cast (выражение)

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

reinterpret_cast

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top