Урок №56. Явное преобразование типов данных

  Юрий  | 

  Обновл. 19 Апр 2019  | 

 23316

 ǀ   8 

Из предыдущего урока мы уже знаем, что компилятор в определённых случаях выполняет неявное преобразование типов данных.

Вступление

Когда вы хотите изменить один тип данных на другой, более крупный (по размеру/диапазону), то неявное преобразование является хорошим вариантом.

Но многие начинающие программисты часто пытаются сделать что-то вроде следующего: float x = 11 / 3;. Однако, поскольку 11 и 3 являются целыми числами, то никакое числовое расширение не происходит. Выполняется целочисленное деление 11 / 3, результатом которого будет значение 3, которое затем неявно преобразуется в 3.0 и присвоится переменной x!

В случае, когда вы используете литералы (такие как 11 или 3), замена одного или обоих целочисленных литералов значением типа с плавающей точкой (11.0 или 3.0) приведёт к конвертации обоих операндов в значения типа с плавающей точкой и выполнится деление типа с плавающей точкой.

Но что будет, если использовать переменные? Например:

Значением переменной x будет 3. Как сообщить компилятору, что мы хотим использовать деление типа с плавающей точкой вместо целочисленного деления? Правильно! Использовать один из операторов явного преобразования типов данных, чтобы указать компилятору выполнить явное преобразование.

Операторы явного преобразования типов данных


В C++ есть 5 типов операторов casts (или ещё «операторов явного преобразования типов»):

   C-style cast;

   static_cast;

   const_cast;

   dynamic_cast;

   reinterpret_cast.

В этом уроке мы рассмотрим C-style cast и static_cast. dynamic_cast мы будем рассматривать, когда дойдём до указателей и наследования. const_cast и reinterpret_cast следует избегать вообще, потому что они полезны только в редких случаях и могут создать немало проблем, если их использовать неправильно.

Правило: Избегайте использования const_cast и reinterpret_cast, если у вас нет на это веской причины.

C-style cast

В программировании на языке C явное преобразование типов данных выполняется с помощью оператора (). Внутри круглых скобок пишем тип, в который нужно конвертировать. Этот способ конвертации типов называется C-style cast. Например:

В программе выше мы используем круглые скобки, чтобы сообщить компилятору преобразовать переменную i1 (типа int) в тип float. Поскольку i1 станет типа float, то i2 затем автоматически преобразуется в тип float также и выполнится деление типа с плавающей точкой!

C++ также позволяет использовать этот оператор и следующим образом:

C-style cast не проверяется компилятором во время компиляции, поэтому может быть неправильно использован, например: при конвертации типов const или изменении типов данных, без учёта их диапазонов (что приведёт к переполнению).

Следовательно, C-style cast лучше не использовать.

Правило: Не используйте C-style cast.

Оператор static_cast


В C++ есть ещё один оператор явного преобразования типов данных — static_cast. Ранее, в уроке о символьном типе данных, мы уже использовали static_cast для конвертации переменной типа char в тип int, выводя вместо символа целое число:

Оператор static_cast лучше всего использовать для конвертации одного фундаментального типа данных в другой:

Основным преимуществом static_cast является проверка компилятором во время компиляции, что усложняет возможность возникновения непреднамеренных проблем. static_cast также (специально) имеет меньшее влияние, чем C-style cast, поэтому вы не сможете случайно изменить тип const или сделать другие вещи, которые не имеют смысла.

Использование операторов явного преобразования в неявном преобразовании

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

Конвертация переменной типа int (4 байта) в тип char (1 байт) потенциально опасна — компилятор выдаст предупреждение. Чтобы сообщить ему, что вы намеренно делаете что-то, что потенциально опасно (но хотите сделать это в любом случае), используйте оператор static_cast:

В следующем случае компилятор будет жаловаться, что конвертация из double в int может привести к потере данных:

Чтобы сообщить компилятору, что мы сознательно хотим сделать это:

Заключение


Преобразования типов данных следует избегать, если это вообще возможно, поскольку всякий раз, когда выполняется подобное изменение, есть вероятность возникновения непредвиденных проблем. Но очень часто случаются ситуации, когда этого не избежать. Поэтому в таких случаях лучше использовать оператор static_cast вместо C-style cast.

Тест

В чём разница между явным и неявным преобразованием типов данных?

Ответ

Неявное преобразование происходит, когда компилятор ожидает значение одного типа, но получает значение другого типа.

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


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (143 оценок, среднее: 4,87 из 5)
Загрузка...

Комментариев: 8

  1. Аватар Алексей:

    Странно, но это

    прошло без всяких проблем.

    1. Аватар Андрей:

      Смотря что Вы подразумеваете под проблемой)
      Если под проблемой Вы подразумеваете ошибку компиляции или рантайма, то да, код корректен и должен отработать, так что не удивительно, что оно "прошло без всяких проблем", однако, в действительности, проблема есть. Вам повезло со значениями и 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;

  2. Аватар Александр:

    пользоваться фокусом с числовыми литералами (11.0 / 3.0 или a / 2.0) также крайне не желательно. При некоторых настройках оптимизации такое деление все равно будет произведено в целых числах

    1. Аватар Константин:

      Оплячки-опляпапапашныя! Александр, а ну, пожалуйста, с этого момента по-подробнее (и что следует предпринять, чтобы не нарваться на " такое деление все равно будет произведено в целых числах " ?)

      1. Аватар Александр:

        В статье же все есть :)))

        Часто пользуются фокусом для вещественного деления:

        и тому подобным. Теоретически такое деление должно производиться в дробных числах. Но при некоторых настройках компилятора (какая-то из О+… не помню точно) этого не происходит. Если писать в аналогичных ситуациях:

        или

        или что угодно другое из статьи, кроме вписывания целых числовых литералов в виде вещественных 🙂

        1. Аватар Константин:

          Благодарствую за ответ!

  3. Аватар somebox:

    Вопрос: а все-таки почему здесь не срабатывает преобразование во float?

    1. Аватар Владимир:

      Из-за приоритетов операций (Урок 38). Сначала выполнится деление, а уже потом присваивание с неявным преобразованием.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *

telegram канал
НОВОСТИ RAVESLI