Вопрос по c++ – Зачем использовать static_cast <int> (x) вместо (int) x?

578

Я слышал, чтоstatic_cast Функция должна быть предпочтительнее C-стиля или простого приведения в стиле функции. Это правда? Зачем?

Возражаю твою честь,asked and answered. Graeme Perrow
Этот вопрос о «встроенном» типы, такие как int, тогда как этот вопрос о типах классов. Это кажется достаточно существенной разницей, чтобы заслуживать отдельного объяснения. user153275
Я не согласен, этот другой вопрос касался описания различий между приведениями в C ++. Этот вопрос о реальной полезности static_cast, который немного отличается. Vincent Robert
На самом деле static_cast - это оператор, а не функция. ThomasMcLeod
Конечно, мы могли бы объединить два вопроса, но что нам нужно сохранить в этом потоке, так это преимущество использования функций над приведением в стиле C, которое в настоящее время упоминается только в однострочном ответе в другом потоке без голосования. , Tommy Herbert

Ваш Ответ

9   ответов
66

In short:

static_cast<>() cast doesn't. static_cast<>() can be spotted easily anywhere inside a C++ source code; in contrast, C_Style cast is harder to spot. Intentions are conveyed much better using C++ casts.

More Explanation:

The static cast performs conversions between compatible types. It is similar to the C-style cast, but is more restrictive. For example, the C-style cast would allow an integer pointer to point to a char.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Since this results in a 4-byte pointer pointing to 1 byte of allocated memory, writing to this pointer will either cause a run-time error or will overwrite some adjacent memory.

*p = 5; // run-time error: stack corruption

In contrast to the C-style cast, the static cast will allow the compiler to check that the pointer and pointee data types are compatible, which allows the programmer to catch this incorrect pointer assignment during compilation.

int *q = static_cast<int*>(&c); // compile-time error

Читайте больше на:
В чем разница между static_cast & lt; & gt; и C стиль литья
а также
Обычное приведение против static_cast против dynamic_cast

Код @ToddLehman в этом изображении использует две цепочки приведений ((uint32_t)(uint8_t)), чтобы добиться того, что байты помимо самого низкого сбрасываются. Для этого есть побитовое и (0xFF &). Использование приведений скрывает намерение.
Я не согласен с этимstatic_cast<>() более читабельно Я имею в виду,sometimes это так, но большую часть времени & # x2014; особенно для базовых целочисленных типов & # x2014; это просто ужасно и излишне многословно. Например: это функция, которая заменяет байты 32-битного слова. Было бы почти невозможно читать с помощьюstatic_cast<uint##>() бросает, но довольно легко понять, используя(uint##) слепки.Picture of code: imgur.com/NoHbGve
@ToddLehman: Спасибо, но я не сказалalways или. (но в большинстве случаев да) В некоторых случаях приведение стиля c более читабельно. Это одна из причин того, что кастинг в стиле c все еще жив и крут в c ++ imho. :) Кстати, это был очень хороший пример
26

чем просто использование static_cast или приведения в стиле C, потому что при использовании приведения в стиле C происходят разные вещи. Операторы приведения в C ++ предназначены для того, чтобы сделать эти операции более явными.

На поверхности приведение к стилям static_cast и C выглядит одинаково, например, при приведении одного значения к другому:

int i;
double d = (double)i;                  //C-style cast
double d2 = static_cast<double>( i );  //C++ cast

Оба они приводят целочисленное значение к двойному. Однако при работе с указателями все усложняется. Некоторые примеры:

class A {};
class B : public A {};

A* a = new B;
B* b = (B*)a;                                  //(1) what is this supposed to do?

char* c = (char*)new int( 5 );                 //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error

В этом примере (1), возможно, все в порядке, потому что объект, на который указывает A, действительно является экземпляром B. Но что, если вы не знаете в тот момент в коде, на что на самом деле указывает? (2) может быть совершенно законным (вы хотите посмотреть только один байт целого числа), но это также может быть ошибкой, и в этом случае ошибка будет хорошей, например (3). Операторы приведения в C ++ предназначены для выявления этих проблем в коде, предоставляя, по возможности, ошибки времени компиляции или выполнения.

Таким образом, для строгого определения значения Вы можете использовать static_cast. Если вы хотите полиморфное приведение указателей во время выполнения, используйте dynamic_cast. Если вы действительно хотите забыть о типах, вы можете использовать reintrepret_cast. И чтобы просто выкинуть const из окна есть const_cast.

Они просто делают код более явным, чтобы вы знали, что вы делаете.

3

static_cast, помимо манипулирования указателями на классы, также может использоваться для выполнения преобразований, явно определенных в классах, а также для выполнения стандартных преобразований между фундаментальными типами:

double d = 3.14159265;
int    i = static_cast<int>(d);
@ToddLehman: последовательность
Зачем кому-то писатьstatic_cast<int>(d)Впрочем, когда(int)d гораздо более кратким и читаемым? (Я имею в виду базовые типы, а не указатели объектов.)
static_cast - это четкое и осознанное решение сделать очень специфический вид конверсии. Поэтому это добавляет ясности намерения. Он также очень удобен в качестве маркера для поиска исходных файлов для конверсий в обзоре кода, ошибке или обновлении.
@ ToddLehman: Я, учитывая, что делать исключения для определенных типов только потому, что они каким-то образом особенные для вас, не имеет никакого смысла для меня, и я также не согласен с вашим представлением о читаемости. Короче не значит более читабельный, как я вижу из картинки, которую вы разместили в другом комментарии.
Контрапункт @ToddLehman: зачем кому-то писать(int)d когдаint{d} гораздо более читабельным? Конструктор или функция, если у вас есть()синтаксис не так быстро превращается в кошмарный лабиринт скобок в сложных выражениях. В этом случае это будетint i{d} вместоint i = (int)d, Гораздо лучше ИМО. Тем не менее, когда мне просто нужно временное выражение, я используюstatic_cast и я никогда не думал, что использовал приведение конструктора. Я использую только(C)casts когда поспешно пишешь отладкуcouts ...
7
Allows casts to be found easily in your code using grep or similar tools. Makes it explicit what kind of cast you are doing, and engaging the compiler's help in enforcing it. If you only want to cast away const-ness, then you can use const_cast, which will not allow you to do other types of conversions. Casts are inherently ugly -- you as a programmer are overruling how the compiler would ordinarily treat your code. You are saying to the compiler, "I know better than you." That being the case, it makes sense that performing a cast should be a moderately painful thing to do, and that they should stick out in your code, since they are a likely source of problems.

Эффективный C ++ Вступление

Интересно, что мне, возможно, придется изменить способ приведения типов в будущем коде для типов POD.
Я думаю так. Все 3 причины относятся к POD, и полезно иметь только одно правило, а не отдельные правила для классов и POD.
Я полностью согласен с этим для классов, но имеет ли смысл использовать приведение типов в стиле C ++ для типов POD?
7

Когда ты пишешь(bar) foo (что эквивалентноreinterpret_cast<bar> foo если вы не предоставили оператор преобразования типов), вы говорите компилятору игнорировать безопасность типов и просто делайте так, как было сказано.

Когда ты пишешьstatic_cast<bar> foo Вы просите компилятор хотя бы проверить, имеет ли смысл преобразование типов и, для целочисленных типов, вставить некоторый код преобразования.

EDIT 2014-02-26

Я написал этот ответ более 5 лет назад, и я ошибся. (См. Комментарии.) Но он все равно получает голоса!

(bar) foo не эквивалентно reinterpret_cast & lt; bar & gt; (foo). Правила для & quot; (TYPE) expr & quot; в том, что он выберет подходящий тип стиля C ++ для использования, который может включать reinterpret_cast.
Хорошая точка зрения. Евро Мичелли дал однозначный ответ на этот вопрос.
23

static_cast означает, что вы не можете случайноconst_cast или жеreinterpret_cast, Что хорошо.

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

вы можете легко искать ключевое слово static_cast в исходном коде, если планируете привести в порядок проект.

Как это поможет привести в порядок проект?
@Mike, который найдет ложные срабатывания - объявление функции с однимint параметр.
Вы также можете выполнять поиск с использованием скобок, таких как & quot; (int) & quot; но хороший ответ и веская причина использовать кастинг в стиле C ++.
Это может дать ложные отрицания: если вы ищете кодовую базу, где вы не единственный автор, вы не найдете C-стиль, который другие могли по каким-либо причинам ввести.
569

что классические броски C не делают различий между тем, что мы называемstatic_cast<>(), reinterpret_cast<>(), const_cast<>(), а такжеdynamic_cast<>(), Эти четыре вещи совершенно разные.

static_cast<>() обычно безопасно. Существует допустимое преобразование в языке или соответствующий конструктор, который делает это возможным. Единственный случай, когда это немного рискованно, это когда вы переходите в унаследованный класс; вы должны удостовериться, что объект действительно является потомком, который, как вы утверждаете, является внешним, по отношению к языку (например, флаг в объекте).dynamic_cast<>() является безопасным, пока проверяется результат (указатель) или учитывается возможное исключение (ссылка).

reinterpret_cast<>() (илиconst_cast<>()) с другой стороны всегда опасно. Вы говорите компилятору: "Поверь мне: я знаю, что это не похоже наfoo (это выглядит так, как будто оно не изменяемое), но это & quot ;.

Первая проблема состоит в том, что почти невозможно определить, какая из них будет происходить при приведении в стиле C, не глядя на большие и разбросанные фрагменты кода и не зная всех правил.

Предположим, что это:

class CMyClass : public CMyBase {...};
class CMyOtherStuff {...} ;

CMyBase  *pSomething; // filled somewhere

Теперь эти два компилируются одинаково:

CMyClass *pMyObject;
pMyObject = static_cast<CMyClass*>(pSomething); // Safe; as long as we checked

pMyObject = (CMyClass*)(pSomething); // Same as static_cast<>
                                     // Safe; as long as we checked
                                     // but harder to read

Однако давайте увидим этот почти идентичный код:

CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert

pOther = (CMyOtherStuff*)(pSomething);            // No compiler error.
                                                  // Same as reinterpret_cast<>
                                                  // and it's wrong!!!

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

Вторая проблема заключается в том, что броски в стиле C слишком сложно найти. В сложных выражениях может быть очень трудно увидеть броски в стиле C. Практически невозможно написать автоматический инструмент, который должен находить приведения в стиле C (например, инструмент поиска) без полноценного внешнего интерфейса компилятора C ++. С другой стороны, легко найти "static_cast" & quot; или "reinterpret_cast & lt;".

pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
      // No compiler error.
      // but the presence of a reinterpret_cast<> is 
      // like a Siren with Red Flashing Lights in your code.
      // The mere typing of it should cause you to feel VERY uncomfortable.

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

Вы не должны использоватьstatic_cast для низложения иерархии наследования, а скорееdynamic_cast, Это вернет либо нулевой указатель, либо действительный указатель.
@ Дэвид Торнли: Я согласен, как правило. Я думаю, что указал на предостережения к использованиюstatic_cast в этой ситуации.dynamic_cast может быть безопаснее, но это не всегда лучший вариант. Иногда вы делаетеknow указатель указывает на данный подтип посредством непрозрачного для компилятора иstatic_cast быстрее. По крайней мере, в некоторых средах,dynamic_cast требует дополнительной поддержки компилятора и времени выполнения (включение RTTI), и вы можете не захотеть включать его только для пары проверок, которые вы можете выполнить самостоятельно. RTTI в C ++ - это только одно возможное решение проблемы.
Ваш прекрасный ответ обращается к телу поста. Я искал ответ на заголовок "зачем использовать static_cast & lt; int & gt; (x) вместо (int) x". То есть для типаint (а такжеint один) зачем использоватьstatic_cast<int> против(int) как единственное преимущество, кажется, с переменными класса и указателями. Просьба, чтобы вы уточнили это.
Ваше утверждение о C бросках является ложным. Все приведения C являются преобразованиями значений, примерно сопоставимыми с C ++.static_cast, С эквивалентreinterpret_cast является*(destination_type *)&то есть взятие адреса объекта, приведение этого адреса к указателю другого типа, а затем разыменование. За исключением случаев типов символов или определенных структурных типов, для которых C определяет поведение этой конструкции, это обычно приводит к неопределенному поведению в C.
@chux, дляint dynamic_cast не применяется, но все остальные причины остаются в силе. Например: скажемv это параметр функции, объявленный какfloat, затем(int)v являетсяstatic_cast<int>(v), Но если вы измените параметр наfloat*, (int)v тихо становитсяreinterpret_cast<int>(v) в то время какstatic_cast<int>(v) является незаконным и правильно отлавливается компилятором.
4

+ - это не только лучшая практика; они предлагают гораздо большую степень гибкости.

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

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

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

Есть несколько других, но это основные, с которыми вы столкнетесь.

Похожие вопросы