Я слышал, что функция static_cast
должна быть предпочтительней для C-стиля или простого кастомизации в стиле функции. Это правда? Почему?
Зачем использовать static_cast <int> (x) вместо (int) x?
Ответ 1
Основная причина в том, что в классических приведениях C нет различия между тем, что мы называем static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
и dynamic_cast<>()
. Эти четыре вещи совершенно разные.
static_cast<>()
обычно безопасен. Существует допустимое преобразование в языке или соответствующий конструктор, который делает это возможным. Единственный случай, когда это немного рискованно, это когда вы переходите в унаследованный класс; вы должны убедиться, что объект на самом деле является потомком, который, как вы утверждаете, является внешним по отношению к языку (например, флаг в объекте). dynamic_cast<>()
безопасен, пока проверяется результат (указатель) или учитывается возможное исключение (ссылка).
С другой стороны, 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 wrong!!!
Как видите, нет простого способа различить две ситуации, не зная много о всех участвующих классах.
Вторая проблема заключается в том, что броски в стиле C слишком сложно найти. В сложных выражениях может быть очень трудно увидеть броски в стиле 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 не только более опасны, но и намного сложнее найти их все, чтобы убедиться в их правильности.
Ответ 2
Один прагматический совет: вы можете легко искать ключевое слово static_cast в исходном коде, если планируете убрать проект.
Ответ 3
Короче:
static_cast<>()
дает вам возможность проверки времени компиляции, C-Style литье не делает.static_cast<>()
можно легко заметить где-нибудь внутри исходного кода на С++; в отличие от C_Style литье сложнее обнаружить.- Намерения передаются гораздо лучше с использованием С++-трансляций.
Подробнее:
Статическое преобразование выполняет преобразования между совместимыми типами. Это похож на C-стиль, но более ограничительный. Например, приведение в стиле C позволит целочисленному указателю указывать на char.
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
Дополнительная информация:
В чем разница между static_cast < > и литью стиля C
и
Обычный листинг против static_cast vs. dynamic_cast
Ответ 4
Вопрос больше, чем просто использовать wither static_cast или C style casting, потому что есть разные вещи, которые случаются при использовании стилей стиля C. Операторы литья С++ предназначены для того, чтобы сделать эти операции более явными.
На поверхности static_cast и стили стиля 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. Но что, если вы не знаете в тот момент кода, что на самом деле указывает? (2) может быть совершенно законным (вам нужно только посмотреть на один байт целого числа), но это также может быть ошибкой, и в этом случае ошибка будет приятной, например (3). Операторы литья С++ предназначены для выявления этих проблем в коде, предоставляя при этом ошибки времени компиляции или времени выполнения.
Итак, для строгой "кавычки значений" вы можете использовать static_cast. Если вы хотите, чтобы полиморфное литье указателей во время выполнения использовало dynamic_cast. Если вы действительно хотите забыть о типах, вы можете использовать reintrepret_cast. И просто выкинуть const из окна есть const_cast.
Они просто делают код более явным, чтобы он выглядел так, будто вы знаете, что вы делаете.
Ответ 5
static_cast
означает, что вы не можете случайно const_cast
или reinterpret_cast
, что хорошо.
Ответ 6
Это о том, какую степень безопасности вы хотите навязать.
Когда вы пишете (bar) foo
(что эквивалентно reinterpret_cast<bar> foo
, если вы не предоставили оператор преобразования типа), вы говорите компилятору, чтобы игнорировать безопасность типа и просто делайте, как сказано.
Когда вы пишете static_cast<bar> foo
, вы просите компилятор, по крайней мере, проверить, что преобразование типа имеет смысл и для интегральных типов вставить код преобразования.
EDIT 2014-02-26
Я написал этот ответ более 5 лет назад, и я понял, что это неправильно. (См. Комментарии.) Но он по-прежнему получает upvotes!
Ответ 7
- Позволяет легко найти приведения в вашем коде, используя grep или аналогичные инструменты.
- Делает ясным, какой тип приведения вы делаете, и привлечение компилятора помогает в его применении. Если вы хотите отбросить только константу, вы можете использовать const_cast, который не позволит вам делать другие типы преобразований.
- Приведения типов по своей природе отвратительны - вы, как программист, игнорируете то, как компилятор обычно будет обращаться с вашим кодом. Вы говорите компилятору: "Я знаю лучше, чем вы". В этом случае имеет смысл, что выполнение приведения должно быть умеренно болезненным, и что они должны выделяться в вашем коде, поскольку они являются вероятным источником проблем.
См. Эффективное C++ Введение
Ответ 8
static_cast, помимо манипулирования указателями на классы, также может использоваться для выполнения преобразований, явно определенных в классах, а также для выполнения стандартных преобразований между основными типами:
double d = 3.14159265;
int i = static_cast<int>(d);
Ответ 9
C Стили стиля легко пропустить в блоке кода. Стили стиля С++ - это не только лучшая практика; они обеспечивают гораздо большую степень гибкости.
reinterpret_cast допускает интеграцию с преобразованиями типа указателя, однако может быть небезопасным при неправильном использовании.
static_cast предлагает хорошее преобразование для числовых типов, например. от enum до int или ints до float или любых типов данных, которые вы уверены в типе. Он не выполняет проверку времени выполнения.
dynamic_cast, с другой стороны, выполнит эти проверки, помечая любые неоднозначные назначения или преобразования. Он работает только с указателями и ссылками и несет накладные расходы.
Есть несколько других, но это основные из них, с которыми вы столкнетесь.