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

  Юрий  | 

  Обновл. 14 Сен 2020  | 

 52703

 ǀ   13 

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

Вступление

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

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

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

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

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

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


В языке 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

В программировании на языке Cи явное преобразование типов данных выполняется с помощью оператора (). Внутри круглых скобок мы пишем тип, в который нужно конвертировать. Этот способ конвертации типов называется конвертацией 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.

Тест

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

Ответ

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

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


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

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

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

  1. Аватар Тимур:

    Вот так нужно использовать конвертацию C-style

    Правило: Не используйте конвертацию C-style.
    )))))

    1. Юрий Юрий:

      Это точно 🙂

  2. Аватар Jonas:

    В VS 2019 static_cast == C-style. Компілятором не провіряється я пробував.

  3. Аватар Эдуард:

    Решил просто попробовать, а оно работает.Код ниже выведет 8-битное число в двоичной системе. Не совсем понимаю, bitset о котором говорилось в уроке 46 — это тоже тип данных? Если же нет, то было бы интересно узнать как и почему это работает и что еще можно использовать с оператором static_cast.

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

      Эдуард, это же аутентичная (для ПК) форма хранения данных!

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

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

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

    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;

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

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

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

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

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

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

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

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

        или

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

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

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

  6. Аватар somebox:

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

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

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

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

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