Для чего используется преобразование типов
Урок №56. Явное преобразование (приведение) типов данных
Обновл. 27 Окт 2021 |
Из предыдущего урока мы уже знаем, что компилятор в определенных случаях выполняет неявное преобразование типов данных. На этом уроке мы рассмотрим использование явной конвертации типов данных.
Зачем использовать явную конвертацию данных?
Когда вы хотите изменить один тип данных на другой, более крупный (по размеру/диапазону), то неявное преобразование является хорошим вариантом.
В случае, когда вы используете литералы (такие как 11 или 3 ), замена одного или обоих целочисленных литералов значением типа с плавающей точкой ( 11.0 или 3.0 ) приведет к конвертации обоих операндов в значения типа с плавающей точкой и выполнится деление типа с плавающей точкой.
Но что будет, если использовать переменные? Например:
Операторы явного преобразования типов данных
В языке C++ есть 5 видов операций явного преобразования типов:
конвертация C-style;
применение оператора static_cast;
применение оператора const_cast;
применение оператора dynamic_cast;
применение оператора reinterpret_cast.
На этом уроке мы рассмотрим конвертацию C-style и оператор static_cast. Оператор dynamic_cast мы будем рассматривать, когда дойдем до указателей и наследования. Применения операторов const_cast и reinterpret_cast следует избегать, так как они полезны только в редких случаях и могут создать немало проблем, если их использовать неправильно.
Правило: Избегайте использования операторов const_cast и reinterpret_cast, если у вас нет на это веских причин.
Конвертация C-style
В программе, приведенной выше, мы используем круглые скобки, чтобы сообщить компилятору о необходимости преобразования переменной i1 (типа int) в тип float. Поскольку переменная i1 станет типа float, то i2 также затем автоматически преобразуется в тип float, и выполнится деление типа с плавающей точкой!
Язык C++ также позволяет использовать этот оператор следующим образом:
Конвертация C-style не проверяется компилятором во время компиляции, поэтому она может быть неправильно использована, например, при конвертации типов const или изменении типов данных, без учета их диапазонов (что может привести к переполнению).
Следовательно, конвертацию C-style лучше не использовать.
Правило: Не используйте конвертацию C-style.
Оператор static_cast
В языке C++ есть еще один оператор явного преобразования типов данных — оператор static_cast. Ранее, на уроке о символьном типе данных char, мы уже использовали оператор static_cast для конвертации переменной типа char в тип int, выводя вместо символа целое число:
Оператор static_cast лучше всего использовать для конвертации одного фундаментального типа данных в другой:
Основным преимуществом оператора static_cast является проверка его выполнения компилятором во время компиляции, что усложняет возможность возникновения непреднамеренных проблем.
Использование операторов явного преобразования в неявном преобразовании
Если вы будете выполнять небезопасные неявные преобразования типов данных, то компилятор будет жаловаться. Например:
Конвертация переменной типа int (4 байта) в тип char (1 байт) потенциально опасна — компилятор выдаст предупреждение. Чтобы сообщить ему, что вы намеренно делаете что-то, что потенциально опасно (но хотите сделать это в любом случае), используйте оператор static_cast:
В следующем случае компилятор будет жаловаться, что конвертация из типа double в тип int может привести к потере данных:
Чтобы сообщить компилятору, что мы сознательно хотим сделать это:
Заключение
Преобразования типов данных следует избегать, если это вообще возможно, поскольку всякий раз, когда выполняется подобное изменение, есть вероятность возникновения непредвиденных проблем. Но очень часто случаются ситуации, когда этого не избежать. Поэтому в таких случаях лучше использовать оператор static_cast вместо конвертации C-style.
В чём разница между явным и неявным преобразованием типов данных?
Ответ
Неявное преобразование происходит, когда компилятор ожидает значение одного типа, но получает значение другого типа.
Явное преобразование происходит, когда программист использует оператор явного преобразования для конвертации значения из одного типа данных в другой.
Поделиться в социальных сетях:
Урок №55. Неявное преобразование типов данных
Комментариев: 14
Вот так нужно использовать конвертацию C-style
…
Правило: Не используйте конвертацию C-style.
)))))
Вон там лежит граната… Но ты её не трогай))
В VS 2019 static_cast == C-style. Компілятором не провіряється я пробував.
Решил просто попробовать, а оно работает.Код ниже выведет 8-битное число в двоичной системе. Не совсем понимаю, bitset о котором говорилось в уроке 46 — это тоже тип данных? Если же нет, то было бы интересно узнать как и почему это работает и что еще можно использовать с оператором static_cast.
Эдуард, это же аутентичная (для ПК) форма хранения данных!
прошло без всяких проблем.
Смотря что Вы подразумеваете под проблемой)
Если под проблемой Вы подразумеваете ошибку компиляции или рантайма, то да, код корректен и должен отработать, так что не удивительно, что оно «прошло без всяких проблем», однако, в действительности, проблема есть. Вам повезло со значениями и 90 делится на 3.6 без остатка, потому имеем 25, но подели Вы 90 на 3.7 (24.32…) или 3.5 (25.71…), или ещё на какое число, данный код выдаст Вам, для 3.7 (24), а для 3.5 (25), хотя остаток есть.
Во второй строке Вы неявно приводите i к типу double при делении, за счёт дробного знаменателя, получаете вещественный результат (по сути, временный rvalue объект, с типом double), который, затем, пытаетесь присвоить переменной, тип которой как был int, так и остался, а, значит, будет произведено приведение вещественного результата к типу int.
Если Вы так и хотели — работать с целочисленным значением, то всё хорошо, в противном же случае стоит сменить тип i, либо, если по какой то причине этого делать не хочется, создать буфер, который будет хранить вещественный результат.
Также, Вашу вторую строчку можно сократить до i /= 3.6;
пользоваться фокусом с числовыми литералами (11.0 / 3.0 или a / 2.0) также крайне не желательно. При некоторых настройках оптимизации такое деление все равно будет произведено в целых числах
В статье же все есть :)))
Часто пользуются фокусом для вещественного деления:
Преобразования типов и безопасность типов
В этом документе описаны распространенные проблемы преобразования типов и описывается, как избежать их использования в коде C++.
Когда компилятор обнаруживает ненадежное преобразование, он выдает ошибку или предупреждение. Произошла ошибка при остановке компиляции. Предупреждение позволяет продолжить компиляцию, но указывает на возможную ошибку в коде. Однако даже если программа компилируется без предупреждений, она по-прежнему может содержать код, который вызывает неявные преобразования типов, приводящие к неправильным результатам. Ошибки типов также могут вводиться явными преобразованиями или приведениями в коде.
Неявные преобразования типов
Если выражение содержит операнды различных встроенных типов и явные приведения отсутствуют, компилятор использует встроенные стандартные преобразования для преобразования одного из операндов, чтобы типы совпадали. Компилятор пытается выполнить преобразования в четко определенной последовательности, пока она не завершится успешно. Если выбранное преобразование является повышением, компилятор не выдает предупреждение. Если преобразование является узким, компилятор выдает предупреждение о возможной утрате данных. Происходит ли фактическая потери данных, зависит от фактических значений, но рекомендуется считать это предупреждение как ошибку. Если включен определяемый пользователем тип, компилятор пытается использовать преобразования, указанные в определении класса. Если не удается найти допустимое преобразование, компилятор выдает ошибку и не компилирует программу. Дополнительные сведения о правилах, регулирующих стандартные преобразования, см. в разделе стандартные преобразования. Дополнительные сведения о пользовательских преобразованиях см. в разделе пользовательские преобразования (C++/CLI).
Расширяющие преобразования (продвижение)
В расширяющем преобразовании значение меньшей переменной присваивается более крупной переменной без потери данных. Поскольку расширяющие преобразования всегда являются надежными, компилятор выполняет их автоматически и не выдает предупреждения. Следующие преобразования являются расширяющими преобразованиями.
Сужающие преобразования (приведение)
Компилятор выполняет сужающие преобразования неявным образом, но предупреждает о возможной потере данных. Выведите эти предупреждения очень серьезно. Если вы уверены, что не произойдет потери данных, так как значения в переменной большего размера всегда помещаются в меньшую переменную, добавьте явное приведение, чтобы компилятор больше не выдавал предупреждение. Если вы не уверены, что преобразование является надежным, добавьте в код какую-либо проверку среды выполнения для обработки возможной потери данных, чтобы она не вызывала неправильные результаты.
Преобразование из типа с плавающей запятой в целочисленный тип является узким преобразованием, так как дробная часть значения с плавающей запятой отбрасывается и теряется.
В следующем примере кода показаны некоторые неявные сужающие преобразования и предупреждения, которые возникают компилятором.
Преобразования со знаком — без знака
Компилятор не предупреждает о неявных преобразованиях между целыми типами со знаком и без знака. Поэтому рекомендуется полностью избегать беззнаковых преобразований. Если вы не можете избежать их, добавьте проверку среды выполнения, чтобы определить, является ли преобразуемое значение большим или равным нулю и меньше или равно максимальному значению типа со знаком. Значения в этом диапазоне будут передаваться из входных файлов в неподписанный или из неподписанных в подписывание без переинтерпретации.
Преобразования указателей
Явные преобразования (приведения)
С помощью операции приведения можно указать компилятору преобразовать значение одного типа в другой тип. В некоторых случаях компилятор вызовет ошибку, если эти два типа полностью не связаны, но в других случаях не вызывает ошибку, даже если операция не является строго типизированной. Используйте приведение с осторожностью, так как любое преобразование из одного типа в другой является потенциальным источником ошибок программы. Однако иногда требуется выполнить приведения, а не все приведения являются опасными. Одно эффективное использование приведения заключается в том, что в коде выполняется понижающие преобразования и известно, что преобразование не приводит к созданию неверных результатов в программе. Фактически, это говорит компилятору о том, что вы делаете, а также о том, что вы выполняете предупреждения. Другой способ заключается в приведении из класса указателя на класс, производный от указатель на базовый. Другой способ — приведение к переменной постоянной, чтобы передать ее в функцию, для которой требуется аргумент, не являющийся константой. Большинство этих операций приведения к некоторым рискам требует определенного риска.
В программировании в стиле C для всех типов приведений используется один и тот же оператор приведения в стиле C.
Этот оператор приведения не используется так часто, как другие, и не гарантирует перенос в другие компиляторы.
Дополнительные сведения см. в разделе оператор.
Преобразование типов в Си
Вы будете перенаправлены на Автор24
Преобразование типов в Си — это преобразование любого типа данных в любой другой тип. При таком преобразовании не имеет значения безопасность этой процедуры.
Введение
Если требуется поменять тип данных на иной, например, больший по размеру, то возможно использовать для этой цели неявный метод преобразования. Некоторые неопытные программисты иногда применяют такой вариант: float x = 11 / 3;. Но поскольку и одиннадцать и три, это целочисленные величины, то никакие преобразования типов чисел выполняться не будет. Будет выполнена операция деления без остатка одиннадцать на три, что даст в итоге три. Тройка пройдёт преобразование в вид 3.0 и это значение получит переменная х. То есть, если применяются числовые символы (например, одиннадцать или три), то подмена первого или пары целых чисел на значения с плавающей точкой (11.0 или 3.0) ведёт к преобразованию их в значении вида с плавающей точкой и выполнению деления типа с плавающей точкой. Оптимально в таком случае использовать переменные.
Переменная х примет в итоге значение три. Чтобы программа компиляции понимала, что программисту надо заменить деление целых чисел без остатка на деление типа с плавающей точкой, следует применять какой-либо оператор, преобразующий типы данных в явной форме.
Явное преобразование типов
В программном языке С++ имеется следующие типы операторов casts, выполняющих в явной форме изменение типов данных:
В программах на языке С изменение в явной форме типа данных осуществляется посредством специального обозначения (). Внутрь скобок заносится тип, в который необходимо преобразовать данные. Такой способ перемены типа данных носит название C-style cast. Применять можно, например, так:
3 float x = (float)i1 / i2;
В этом примере содержимое внутри скобок сообщает программе компиляции, что надо выполнить изменение переменной i1 (типа int) в тип float. Так как i1 теперь имеет тип float, то i2 тоже будет автоматом преобразовано в такой же тип, и операция деления уже будет выполнена для типа данных с плавающей точкой. Следует заметить, не стоит применять данный оператор для изменения данных типов const, так как это может привести к ошибкам.
Готовые работы на аналогичную тему
Оператор static_cast
Этот оператор может применяться для конвертирования переменной типа char в тип int, при этом выводится целое число, а не символ:
Рисунок 1. Код. Автор24 — интернет-биржа студенческих работ
Этот оператор оптимально применять для преобразования фундаментальных типов(один в другой):
3 float x = static_cast (i1) / i2;
Главным достоинством static_cast может считаться наличие проверки программой компиляции на ошибки, что предотвращает появление непредсказуемых проблем. Кроме того, оператор static_cast обладает меньшим влиянием, чем оператор C-style cast. Это исключает возможность случайного изменения типа const и другие бессмысленные операции.
Использование операторов явного преобразования в неявном преобразовании
При использовании потенциально опасных неявных преобразований типов данных, программа компиляции может выдавать предупреждения. К примеру, такие:
2 char ch = i; // неявное преобразование
Преобразование переменной типа int (4 байта) в тип char (1 байт) несёт потенциальную опасность, и компилятор об этом предупредит. Чтобы компилятор понял, что программист понимает потенциальную опасность операции, но её надо всё равно выполнить, следует применять оператор static_cast:
Рисунок 2. Код. Автор24 — интернет-биржа студенческих работ
В примере ниже, программа компиляции может выдать сообщение, что преобразование из double в int способно спровоцировать потерю данных:
Для сообщения компилятору о сознательном намерении выполнить операцию, следует записать такие команды:
Рисунок 3. Код. Автор24 — интернет-биржа студенческих работ
Не следует использовать без крайней необходимости конвертацию типов данных. Почти всегда при выполнении этих преобразований существует вероятность появления различных проблем. Но если это всё-таки необходимо сделать, лучше применять static_cast вместо C-style cast.
Конвертирование типов в арифметических операциях
При использовании в арифметических операциях разных типы значений, программа компиляции будет пытаться в неявной форме преобразовать их в один тип. А так же, если программа задаёт переменной конкретное значение, оно всегда должно быть приведено к типу, присущему этой переменной. К примеру, так:
Переменная d, представляющая тип int, получает значение типа char. По этой причине компилятор приведёт её значение от типа char к типу int. Как отмечалось выше, такие преобразования могут нести потенциальную опасность, так как различные типы обладают различным внутренним представлением. И без потерь данных изменение типа представления не всегда можно осуществить. Программа компиляции при арифметических операциях делает следующие преобразования:
3 double c = a + b; // 14.000000
В формуле a + b переменная b имеет тип double, по этой причине переменная a так же в автомате преобразуется к типу double. Итог арифметической операции сложения также станет типа double.
Заданные пользователем преобразования типов (C++)
Преобразование создает новое значение некоторого типа из значения другого типа. Стандартные преобразования встроены в язык C++ и поддерживают встроенные типы. Кроме того, можно создавать пользовательские преобразования для выполнения преобразований в типы, из или между определяемыми пользователем типами.
Стандартные преобразования выполняют преобразование между встроенными типами, между указателями или ссылками на типы, связанные наследованием, в и из указателей void и в пустой указатель. Дополнительные сведения см. в разделе стандартные преобразования. Пользовательские преобразования выполняют преобразование между пользовательскими типами или между пользовательскими и встроенными типами. Их можно реализовать как конструкторы преобразования или как функции преобразования.
Преобразования могут быть явными, когда программист вызывает преобразование одного типа в другой (как в приведении или прямой инициализации) или неявными, когда язык или программа вызывают типы, которые отличаются от заданных программистом.
Попытка неявного преобразования выполняется, когда
тип аргумента, предоставленного для функции, не совпадает с соответствующим параметром;
тип значения, возвращаемого функцией, не совпадает с типом возвращаемого значения функции;
тип выражения инициализатора не совпадает с типом инициализируемого объекта;
тип результата выражения, которое управляет условным оператором, циклической конструкцией или параметром, не совпадает с тем, который требуется для управления;
тип операнда, предоставленного для оператора, не совпадает с соответствующим параметром операнда. Для встроенных операторов тип обоих операндов должен совпадать; он преобразуется в общий тип, который может представлять оба операнда. Дополнительные сведения см. в разделе стандартные преобразования. Для пользовательских операторов тип каждого операнда должен совпадать с соответствующим параметром операнда.
Если не удается выполнить неявное преобразование с помощью стандартного преобразования, компилятор может использовать пользовательское преобразование, за которым (при необходимости) будет следовать дополнительное стандартное преобразование.
Если на сайте преобразования есть два и более пользовательских преобразования, выполняющих одно преобразование, преобразование называется неоднозначным. Неоднозначность подразумевает ошибку, так как компилятор не может определить, какое из доступных преобразований выбрать. Тем не менее, не будет ошибкой определить несколько способов выполнения одного преобразования, так как набор доступных преобразований может отличаться в разных участках исходного кода, например в зависимости от того, какие файлы заголовков входят в исходный файл. Пока на сайте преобразования доступно только одно преобразование, о неоднозначности речь не идет. Существует несколько путей возникновения неоднозначных преобразований, однако самые распространенные перечислены ниже.
Множественное наследование. Преобразование определено в нескольких базовых классах.
Вызов неоднозначной функции. Преобразование определено как конструктор преобразования типа целевого объекта и как функция преобразования типа источника. Дополнительные сведения см. в разделе функции преобразования.
Неоднозначность, как правило, можно устранить, просто более полно указав имя соответствующего типа или выполнив явное приведение для пояснения намерения.
Конструкторы преобразования и функции преобразования подчиняются правилам управления доступом членов, однако доступность преобразований учитывается, только если можно определить неоднозначное преобразование. Это означает, что преобразование может быть неоднозначным, даже если уровень доступа конкурирующего преобразования будет блокировать его использование. Дополнительные сведения о специальных возможностях членов см. в разделе Управление доступом к членам.
Ключевое слово explicit и проблемы с неявным преобразованием
По умолчанию при создании пользовательского преобразования компилятор может использовать его для выполнения неявных преобразований. Иногда это совпадает с вашими намерениями, но в других случаях простые правила, которые определяют выполнение неявных преобразований компилятором, могут привести к тому, что он примет нежелательный код.
explicit Ключевое слово можно применять к конструкторам преобразования с c++ 98, а также к функциям преобразования, начиная с c++ 11. В следующих разделах содержатся дополнительные сведения об использовании explicit ключевого слова.
Конструкторы преобразования
Объявление конструкторов преобразования
Следующие правила применяются к объявлению конструктора преобразования.
Целевым типом преобразования является сконструированный пользовательский тип.
Конструкторы преобразований, как правило, принимают только один аргумент типа источника. Однако конструктор преобразования может указывать дополнительные параметры, если у каждого из них есть значение по умолчанию. Тип источника остается типом первого параметра.
Конструкторы преобразований, как и все конструкторы, не указывают тип возвращаемого значения. Указание типа возвращаемого значения в объявлении является ошибкой.
Конструкторы преобразования могут быть явными.
Явные конструкторы преобразования
Несмотря на то, что использование неявных преобразований кажется удобным, в результате могут возникать трудновыявляемые ошибки. Как показывает опыт, лучше всего объявлять все конструкторы преобразований явными за исключением тех случаев, когда необходимо, чтобы определенное преобразование выполнялось неявно.
Функции преобразования
Функции преобразования определяют преобразования из пользовательского в другие типы. Эти функции иногда называют «операторами приведения», так как они, наряду с конструкторами преобразования, вызываются, когда значение приводится к другому типу. В следующем примере демонстрируется функция преобразования, преобразующая из определяемого пользователем типа, Money в встроенный тип double :
Объявление функций преобразования
Следующие правила применяются к объявлению функции преобразования.
Целевой тип преобразования должен быть объявлен до объявления функции преобразования. Классы, структуры, перечисления и определения типа нельзя объявлять в объявлении функции преобразования.
Функции преобразования не принимают аргументов. Указание любых параметров в объявлении является ошибкой.
Функции преобразования имеют тип возвращаемого значения, задаваемый именем функции преобразования, которое также является именем типа целевого объекта преобразования. Указание типа возвращаемого значения в объявлении является ошибкой.
Функции преобразования могут быть виртуальными.
Функции преобразования могут быть явными.
Явные функции преобразования
Если функция преобразования объявлена как явная, ее можно использовать только для выполнения явного приведения. Это не дает функциям, которые принимают аргумент типа целевого объекта функции преобразования, также неявно принимать аргументы типа класса, а также блокирует инициализацию копирования экземпляров типа целевого объекта из значения типа класса. В следующем примере демонстрируется определение явной функции преобразования и влияние на правильный синтаксис кода.
Здесь оператор функции преобразования Double был явно объявлен, а явное приведение к типу было представлено в функции display_balance для выполнения преобразования. Если пропустить это преобразование, компилятор не сможет найти подходящий оператор вставки в поток для типа Money и может возникнуть ошибка.