Я слышал, что эта static_cast
функция должна быть предпочтительнее C-стиля или простого преобразования в стиле функции. Это правда? Почему?
Я слышал, что эта static_cast
функция должна быть предпочтительнее C-стиля или простого преобразования в стиле функции. Это правда? Почему?
Ответы:
Основная причина заключается в том , что классические слепки C не делают никакого различия между тем, что мы называем static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
и dynamic_cast<>()
. Эти четыре вещи совершенно разные.
А static_cast<>()
обычно безопасно. Существует допустимое преобразование в языке или соответствующий конструктор, который делает это возможным. Единственный случай, когда это немного рискованно, это когда вы переходите в унаследованный класс; вы должны убедиться, что объект на самом деле является потомком, который, как вы утверждаете, является внешним по отношению к языку (например, флаг в объекте). A 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's 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 не только более опасны, но и намного сложнее найти их все, чтобы убедиться в их правильности.
static_cast
для приведения иерархии наследования, а скорее dynamic_cast
. Это вернет либо нулевой указатель, либо действительный указатель.
static_cast
в этой ситуации. dynamic_cast
может быть безопаснее, но это не всегда лучший вариант. Иногда вы знаете, что указатель указывает на данный подтип, непрозрачный для компилятора, и static_cast
быстрее. По крайней мере, в некоторых средах dynamic_cast
требуется дополнительная поддержка компилятора и затраты времени выполнения (включение RTTI), и вы можете не захотеть включать его только для пары проверок, которые вы можете выполнить самостоятельно. RTTI в C ++ - это только одно из возможных решений проблемы.
static_cast
. Эквивалент C reinterpret_cast
- это *(destination_type *)&
, например, взятие адреса объекта, приведение этого адреса к указателю другого типа, а затем разыменование. За исключением случаев типов символов или определенных структурных типов, для которых C определяет поведение этой конструкции, это обычно приводит к неопределенному поведению в C.
int
(и int
отдельно), почему использование static_cast<int>
vs. (int)
в качестве единственного преимущества, кажется, с переменными класса и указателями. Просьба, чтобы вы уточнили это.
int
dynamic_cast
не применяется, но все остальные причины остаются в силе . Например: скажем v
, это параметр функции, объявленный как float
, то (int)v
есть static_cast<int>(v)
. Но если вы измените параметр на float*
, (int)v
спокойно становится, reinterpret_cast<int>(v)
пока static_cast<int>(v)
незаконно и правильно отлавливается компилятором.
Один прагматичный совет: вы можете легко найти ключевое слово static_cast в исходном коде, если планируете привести в порядок проект.
int
параметром.
Короче говоря :
static_cast<>()
дает вам возможность проверки времени компиляции, а C-Style - нет.static_cast<>()
может быть легко обнаружен в любом месте исходного кода C ++; напротив, бросок C_Style труднее обнаружить.- Намерения передаются намного лучше, используя приведения C ++.
Более объяснение :
Статическое приведение выполняет преобразования между совместимыми типами . Это похоже на приведение в стиле C, но более ограничено. Например, приведение в стиле C позволит целочисленный указатель указывать на символ.
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 и dynamic_cast
static_cast<>()
более читабельно. Я имею в виду, иногда это так, но в большинстве случаев - особенно для базовых целочисленных типов - это просто ужасно и излишне многословно. Например: это функция, которая заменяет байты 32-битного слова. Было бы почти невозможно читать с использованием static_cast<uint##>()
приведений, но это довольно легко понять с помощью (uint##)
приведений. Изображение кода: imgur.com/NoHbGve
always
. (но в большинстве случаев да) В некоторых случаях приведение стиля c более читабельно. Это одна из причин того, что кастинг в стиле c все еще жив и крут в c ++ imho. :) Кстати, это был очень хороший пример
(uint32_t)(uint8_t)
) для достижения сброса байтов, кроме младших. Для этого есть побитовое и ( 0xFF &
). Использование приведений скрывает намерение.
Вопрос больше, чем просто использование 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.
Они просто делают код более явным, чтобы вы знали, что делаете.
static_cast
означает, что вы не можете случайно const_cast
или reinterpret_cast
, что хорошо.
См. Эффективное введение C ++
Речь идет о том, какую безопасность типов вы хотите навязать.
Когда вы пишете (bar) foo
(что эквивалентноreinterpret_cast<bar> foo
что вы не указали оператор преобразования типа), вы говорите компилятору игнорировать безопасность типов и просто делаете, как было сказано.
Когда вы пишете, static_cast<bar> foo
вы просите компилятор хотя бы проверить, имеет ли смысл преобразование типов и, для целочисленных типов, вставить некоторый код преобразования.
РЕДАКТИРОВАТЬ 2014-02-26
Я написал этот ответ более 5 лет назад, и я ошибся. (См. Комментарии.) Но он все равно получает голоса!
static_cast<bar>(foo)
с круглыми скобками. То же самое для reinterpret_cast<bar>(foo)
.
Броски в стиле C легко пропустить в блоке кода. Приведение в стиле C ++ - это не только лучшая практика; они предлагают гораздо большую степень гибкости.
reinterpret_cast позволяет интегрировать преобразования типа указателя, однако может быть небезопасным при неправильном использовании.
static_cast предлагает хорошее преобразование для числовых типов, например, от перечислений до целых чисел или от целых чисел до чисел с плавающей точкой или любых типов данных, которые вы уверены в типе. Он не выполняет никаких проверок во время выполнения.
С другой стороны, dynamic_cast будет выполнять эти проверки, отмечая любые неоднозначные назначения или преобразования. Он работает только с указателями и ссылками и несет накладные расходы.
Есть несколько других, но это основные, с которыми вы столкнетесь.
static_cast, помимо манипулирования указателями на классы, также может использоваться для выполнения преобразований, явно определенных в классах, а также для выполнения стандартных преобразований между основными типами:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
, хотя, когда (int)d
он намного более лаконичен и читабелен? (Я имею в виду базовые типы, а не указатели на объекты.)
(int)d
когда int{d}
он намного лучше читается? ()
Синтаксис конструктора или функционально-подобного, если у вас есть , не так быстро превратится в кошмарный лабиринт скобок в сложных выражениях. В этом случае это было бы int i{d}
вместо int i = (int)d
. Гораздо лучше ИМО. Тем не менее, когда мне просто нужно временное выражение, я использую static_cast
и никогда не использовал приведение конструктора, я не думаю. Я использую только (C)casts
когда поспешно пишу отладку cout
с ...