Сведения о вопросе

Solllo

10:08, 2nd August, 2020

Теги

c++   pointers   casting    

Завсегдатаев и метод static_cast и динамическое приведение dynamic_cast

Просмотров: 585   Ответов: 8

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

MyClass *m = (MyClass *)ptr;

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

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



  Сведения об ответе

9090

00:42, 16th August, 2020

метод 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 полезно, когда вы не знаете, что такое динамический тип объекта. Он возвращает указатель null, если объект, на который ссылаются, не содержит тип, приведенный к базовому классу (при приведении к ссылке в этом случае создается исключение 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. Приведение в стиле C в основном идентично опробованию ряда последовательностей приведений C++ и взятию первого приведения C++, которое работает, даже не рассматривая dynamic_cast . Излишне говорить , что это намного мощнее , поскольку он сочетает в себе все const_cast, static_cast и reinterpret_cast, но это также небезопасно, потому что он не использует dynamic_cast .

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

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


  Сведения об ответе

baggs

02:02, 2nd August, 2020

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

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

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 error

Переинтерпретация литого

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

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

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

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

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

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

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

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

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
}

Динамический или статический бросок

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

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

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

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

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

// 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

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

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

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

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

print(nonConst); // allowed

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


  Сведения об ответе

baggs

20:23, 10th August, 2020

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

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

const_cast

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

метод static_cast

static_cast (выражение) static_cast<>() используется для приведения между целочисленный тип. 'e.g.' Чара->длинный, инт-короче и т. д.

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

динамическое приведение dynamic_cast

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

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

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

оператора reinterpret_cast

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


  Сведения об ответе

lool

06:53, 11th August, 2020

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

Стиль Барна Строструпа C++ FAQ

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


  Сведения об ответе

dumai

18:02, 29th August, 2020

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

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


  Сведения об ответе

fo_I_K

08:07, 15th August, 2020

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

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


  Сведения об ответе

PIRLO

06:16, 29th August, 2020

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


  Сведения об ответе

#hash

17:38, 20th August, 2020

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться