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

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

  •  01-07-2019
  •  | 
  •  

Вопрос

Я слышал, что static_cast функцию следует предпочесть приведению в стиле C или простому приведению в стиле функции.Это правда?Почему?

Это было полезно?

Решение

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

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

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

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

Давайте предположим, что это:

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

CMyBase  *pSomething; // filled somewhere

Теперь эти два скомпилированы одним и тем же способом:

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

pMyObject = (CDerivedClass*)(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 ++.С другой стороны, легко выполнить поиск по "static_cast<" или "reinterpret_cast<".

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

Короче говоря:

  1. static_cast<>() предоставляет вам возможность проверки во время компиляции, в отличие от C-Style cast этого не делает.
  2. static_cast<>() может быть легко обнаружен в любом месте исходного кода C ++;напротив, приведение C_Style сложнее обнаружить.
  3. Намерения передаются намного лучше с помощью приведений 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 error

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

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

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

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

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

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.Но что, если на этом этапе кода вы не знаете, на что на самом деле указывает a?(2) возможно, это совершенно законно (вы хотите просмотреть только один байт целого числа), но это также может быть ошибкой, и в этом случае ошибка была бы приятной, например (3).Операторы приведения C ++ предназначены для выявления этих проблем в коде путем предоставления ошибок во время компиляции или выполнения, когда это возможно.

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

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

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

  1. Позволяет легко находить приведения в вашем коде с помощью grep или аналогичных инструментов.
  2. Делает явным, какого рода приведение вы выполняете, и привлекает помощь компилятора в его применении.Если вы хотите только отбросить постоянство, то вы можете использовать const_cast , который не позволит вам выполнять другие типы преобразований.
  3. Приведения по своей сути уродливы - вы, как программист, отменяете то, как компилятор обычно обрабатывал бы ваши код.Вы говорите компилятору: "Я знаю лучше тебя". В таком случае это имеет смысл что выполнение приведения должно быть умеренно болезненным делом, и что они должны выделяться в вашем коде, поскольку они являются вероятным источником проблем.

Видишь Эффективный C ++ Введение

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

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

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


РЕДАКТИРОВАТЬ 2014-02-26

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

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

double d = 3.14159265;
int    i = static_cast<int>(d);

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

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

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

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

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

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