Зачем использовать static_cast <int>(x) вместо (int) x? - PullRequest
596 голосов
/ 19 сентября 2008

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

Ответы [ 9 ]

583 голосов
/ 19 сентября 2008

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

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

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

Первая проблема заключается в том, что почти невозможно определить, какой из них произойдет при приведении в стиле 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 <» или «reinterpret_cast <». </p>

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 более опасны, но гораздо сложнее найти их всех, чтобы убедиться, что они правильные.

109 голосов
/ 19 сентября 2008

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

68 голосов
/ 09 октября 2014

Короче :

  1. static_cast<>() дает вам возможность проверки времени компиляции, C-Style бросок не.
  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, статическое приведение позволит компилятор, чтобы проверить, что указатель и pointee типы данных совместимый, что позволяет программисту поймать это неправильно назначение указателя во время компиляции.

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

Подробнее о:
В чем разница между static_cast <> и кастом в стиле C
и
Обычное приведение против static_cast против dynamic_cast

27 голосов
/ 19 сентября 2008

Вопрос больше, чем просто использование статического вещания или приведения в стиле 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.

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

24 голосов
/ 19 сентября 2008

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

7 голосов
/ 19 сентября 2008
  1. Позволяет легко находить слепки в ваш код с использованием grep или аналогичного инструменты.
  2. Явно указывает, какой актерского состава вы делаете, и привлекает помощь компилятора в его применении. Если вы хотите отбросить константа, то вы можете использовать const_cast, который не позволит вам делать другие типы преобразований.
  3. Заклинания по своей природе безобразны - вы как программист отвергает, как Компилятор будет обычно относиться к вашему код. Вы говорите Компилятор «Я знаю лучше, чем вы». В таком случае имеет смысл что выполнение броска должно быть умеренно болезненная вещь, и что они должны торчать в вашем код, так как они являются вероятным источником проблем.

См. Эффективный C ++ Введение

7 голосов
/ 19 сентября 2008

Речь идет о том, какую безопасность типов вы хотите навязать.

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

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


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

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

4 голосов
/ 19 сентября 2008

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

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

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

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

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

4 голосов
/ 19 сентября 2008

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

double d = 3.14159265;
int    i = static_cast<int>(d);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...