В С++ можно явно приводить типы с помощью нескольких кастов. В зависимости от вида преобразования, выбирают одно из четырёх. Часто для работы с самыми обычными типами выбирают операцию static_cast.
Для того, чтобы вообще понимать страшно выглядящий, но очень несложный, синтакисис её применения:
Моя непосредственно текущая статья не полностью раскрывает тему static_cast, а только даёт волшебный пинок к пониманию. Поэтому, после ознакомления с общим принципом, Вам потребуется поизучать информацию в разных источниках (лучше почитать книги авторитетных авторов).
Я не могу сейчас раскрыть тему полностью, потому что в настоящий момент, путь "самурая", рискнувшего изучать язык С++, следующего порядку тем моего сайта, ограничивается препятствиями из самых обычных функций. Мы ещё не дошли до классов. А при работе с классами и применением наследования, у static_cast есть ещё некоторые правила. По этой причине я только очень поверхностно знакомлю читателя с описываемым в статье способе преобразований типов. Имейте это в виду.
В С++ иногда срабатывают неявные преобразования типов. Как правило, они доставляют проблем приходящим в С++ новичкам. Поверхностное ознакомление с неявными преобразованиями описывалось в статье С++ Неявные преобразования численных типов. Но ещё бывает нужда в ручном преобразовании неподходящего типа к нужному. На самом деле, чем больше Вы стараетесь избежать явных привидений типов, тем лучше. В идеале не использовать ручное приведение типов вообще. Но тем не менее, Вы неоднократно ещё будете преобразовывать типы.
В С++ возможно использовать явное приведение в стиле языка С и в стиле языка С++. В стиле языка С++ есть такие вот варианты:
dynamic_cast
static_cast (этот сейчас описывается)
const_cast
reinterpret_cast
Создатель языка С++: Бьярне Страуструппе — считал, что приведения в стиле языка С и в функциональном стиле обладают ужасным свойством: позволяют не вникать в то, что именно они делают. Поэтому в С++ была добавлена возможность явных приведений с помощью операций, перечисленных выше. Эти операции ограничены в своих возможностях, а их непонятные (поначалу) названия хорошо отображают цель использования преобразования и помогают избежать некоторых ошибок ещё на стадии написания кода.
Так как эта статья ограничена static_cast, то дальше будет написано только про static_cast и, как было сказано ранее, только частично.
Схема приведения static_cast выглядит так:
Демонстрация желания преобразования типов
<
Тип, к которому преобразовываем
>
(
Значение, из которого выделяем тип
)
;
Комментарий
static_cast
<
int
>
(
155.55
)
;
Преобразование: из double в int
static_cast
<
double
>
(
value1
)
;
Преобразование: из выделенного из value1 типа в double
static_cast
<
MyType
>
(
value2
)
;
Преобразование: из выделенного из value2 типа в MyType
C++
1
2
3
4
5
6
static_cast<int>(92.99);//Приведение double к int
charx;
static_cast<double>(x);//Приведение char к double
static_cast<void*>(foo());//Приведение типа, полученного от функции к void*
Поскольку язык С++ является надмножеством языка С, с целью совместимости с очень большим числом уже написанных ещё до начала существования С++ программ, многое из языка С осталось в С++. Иногда говорят, что это оставшееся — тяжёлое наследство из языка С. Среди оставленного есть и возможность явного приведения. Такие явные приведения, по правилам языка С, проще использовать, потому что легко понять и запомнить, но они опасные, потому что программист может не понимать, что происходит.
В стиле языка С для явного преобразования типов используюся круглые скобки:
C++
1
2
3
intx=200;
(double)x;//С-style
x(double);//C++ style, reinterpret_cast
Такое приведение считается экстримальным и использовать его очень не рекомендуется. Опасно. Этому стоит предпочитать те страшные касты, которые введены в С++ и жутко выглядят (static_cast, const_cast, dynamic_cast…).
Вернёмся к static_cast. И посмотрим, почему он лучше более простого по написанию варианта:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
usingnamespacestd;
intfoo_int(){
return0;
}
intmain()
{
(void*)foo_int();//компилируется
static_cast<void*>(foo_int());//ошибка компиляции
}
Введённые в С++ касты (приёмы преобразований типов) не дают программе собраться, если преобразование черезчур опасное. Эти ограничения повышают надёжность готовых продуктов.
В показанном примере происходит попытка приведения типа int, отдаваемого функцией foo_int к типу void*. В случае с С-style приведением ошибок никаких нет и программа успешно запустится, но в ходе её работы могут возникнуть проблемы.
Поговорим немного о более сложных для новичков вещах. Cогласно документации языка С++, операция static_cast позволяет явно преобразовать указатели связанных типов один в другой. Если программист использует static_cast для приведения встроенных в С++ примитивных типов, то операцией static_cast используются правила приведения встроенных типов. Если приведение не допускается, то static_cast не даст скомпилироваться программе, что и было продемонстрировано выше: приведение целочисленного типа к указателю на этапе компиляции не удалось. Для собственного удобства первое время можете считать, что если не можете присвоить в одну переменную значение какого-то выражения, то и static_cast не станет приводить тип того выражения к типу той переменной.
C++
1
2
3
constchar*S=newchar[100];
(int)S;//компилируется
static_cast<S>(int);//ошибка компиляции
Причины некомпилирования достаточно просты. Нельзя привести тип, который по сути есть строка, к типу число. У этих типов разное внутренне представление. Хотя приведение в стиле С плюёт на внутренние представления типов и игнорирует сам факт собственного неумения выводить число из строки в неявном виде. static_cast более умный, скомпилироваться программе не даёт. Вы если не поняли, то смотрите, в чём опасность:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <cstring>
intrand(){}
intmain(intargc,char*argv[])
{
char*S=newchar[100];
strcpy(S,"10");
inta=(int)S;//компилируется
std::cout<<a<<'\n';//<--- Ошибка, здесь не 10
delete[]S;
}
Хоть программа и собралась, результат работы плачевен. А static_cast просто бы не дал компилятору собрать программу, предупредив возникновение нехорошей ошибки. Он молодец.
Язык С++ позволяет создавать свои собственные типы данных.
В случае с типами, определёнными программистом, используются правила приведения, описанные программистом.
В примерах статьи я применял указатель на void
К типу void можно привести любой тип.
C++
1
2
chartemp[100];
static_cast<void>(temp);//допускается, к void можно привести всё.
Поскольку нельзя создать обычную переменную с типом void, но можно создать указатель, указывающий на сущность с void типом, я и задействовал в примере указатель на сущность с void типом. Это, если вы будете перечитывать статью, то просто маленькое прояснение.
Я полагаю, что для поверхностного ознакомления этой информации должно хватить и что вы сможете теперь без труда и применять этот каст, и понимать, зачем другие пишут именно так. Дальнейшее знакомство всё глубже и глубже окунает в изучение классов.
Если нужно выполнять приведение каких-то простых типов, встроенных в С++, то приведение с помощью static_cast оправдано.
Но не надо себе воображать, что использование static_cast вообще хорошо. Любое собственноручное приведение типов черевато последствиями. Нужно максимально избегать необходимости приведения типов в серьёзных программах.
Советую отложить где-то закладку, что информация о static_cast была получена не в полном объёме, что после изучения полиморфизма, нужно вернуться к этому способу приведения типов.
Некоторые полезные сведения о static_cast:
Операция static_cast не может удалять атрибуты const, volatile.
Операция static_cast выполнятся во время компиляции программы.
Операция static_cast использует встроенные правила приведения для встроенных типов и программистские правила приведения в программистских типах.
Обычно static_cast используется, когда требуется преобразовать числовые типы данных.
А ещё нужно знать немного о приоритете операций и понимать, что эти приоритеты не отменял никто:
C++
1
2
3
4
5
6
7
8
9
#include <iostream>
usingnamespacestd;
intmain()
{
cout<<static_cast<double>(5/2)<<'\n';//Вывод 2
cout<<static_cast<double>(5)/2<<'\n';//Вывод 2.5
}
В первом случае сначала вычисляется выражение в круглых скобках, а потом происходит преобразование полученного в результате вычисления типа к типу double
Во втором случае сначала тип, выводимый из числа 5, т. е. int приводится к типу double, после чего целое double делится на 2, т. е. на int. Результат double.
На заметку:
В некоторых случаях static_cast используют для обхода предупреждений о приведениях с потерей точности.
Добавить комментарий