Приведение типа

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Приведе́ние ти́па (type conversion) — преобразование значения переменной одного типа в значение другого типа. Выделяют явное и неявное приведения типов.

  • При явном приведении указывается тип переменной, к которому необходимо преобразовать исходную переменную.
  • При неявном приведении преобразование происходит автоматически, по правилам, заложенным в данном языке программирования.
  • Также в языке могут быть заданы специальные функции для приведения.

Неявное приведение[править | править вики-текст]

Само приведение происходит как во время присваивания значения переменной, так и при операциях сравнения, вычисления выражения. При использовании в выражении несколько различных типов значения одного или нескольких подтипов может быть осуществлено преобразование к более общему типу (супертипу), с большим диапазоном возможных значений.

В языке C:

 double  d;  // вещественный тип
 long    l;  // целый тип
 int     i;  // целый тип
 
 if (d > i)      d = i;
 if (i > l)      l = i;
 if (d == l)     d *= 2;

Каждый раз при выполнении операции сравнения или присваивания переменные разных типов будут приведены к единому типу. Следует с осторожностью использовать неявное приведение типа. При переводе числа из вещественного типа в целочисленный, дробная часть отсекается. Обратное приведение из целочисленного типа к вещественному также может привести к понижению точности, что связано с различным представлением вещественных и целочисленных чисел на машинном уровне. К примеру, вещественный тип single стандарта IEEE 754 не может точно представить число 16777217, в то время как 32-битный целочисленный тип может. Это может привести к ситуациям, когда сравнение на равенство одного и того же числа, представленного типами (int и single) будет выдавать ложный результат (числа не равны друг другу).

Явное приведение типа[править | править вики-текст]

В языке C[править | править вики-текст]

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

 int X;
 int Y = 200;
 char C = 30;
 X = (int)C * 10 + Y;    //переменная С приведена к типу int.

Грубых ошибок в данном примере от автоматического приведения типов не произойдет, так как переменная C, которая имеет тип char будет приведена к типу int, так как здесь идет речь о «повышении» типа переменной к старшему в выражении(переменная С перед присваиванием неявно приводится к типу переменной Y). Но возможен один нюанс - в зависимости от машинной реализации типа char при преобразовании char в int может получиться отрицательное число, потому рекомендуется использовать явное преобразование, а если все-таки есть преобразования типа char в int, char объявлять как беззнаковый (unsigned char).

В языке C++[править | править вики-текст]

В языке C++ существует четыре разновидности приведения типа. Все четыре типа записываются в виде

 xxx_cast<type_to>(expression_from)

Например:

 y = static_cast<signed short>(65534);    // будет присвоено -2

Громоздкие ключевые слова являются напоминанием программисту, что приведение типа чревато проблемами.

static_cast[править | править вики-текст]

  • Назначение: Приведение типа по обычным правилам, когда компилятор отказывается привести его автоматически (отличается от применяемого в Си (type_to)expression_from только тем, что с указателями на произвольные типы не работает; для произвольных указателей применяется reinterpret_cast). Применяется:
    • для вычислений в более широком числовом типе (например, для дробных вычислений с целыми числами);
    • чтобы избавиться от предупреждения «Возможная потеря точности» при переводе в более узкий числовой тип;
    • для перевода указателей из/в void*;
    • для указателей и ссылок при конвертации в родительский тип;
    • для типов с конструкторами или операциями конвертации наподобие operator type_to;
    • в шаблонах — компилятор уже при специализации шаблона решает, какие операции использовать;
    • в операции ?:, у которой then- и else-части должны иметь один тип.
  • Ограничения на expression_from: нет.
  • Ограничения на type_to: должен найтись способ преобразования в type_to.
  • Производит ли код: в общем случае да.
  • Возможные ошибки: относительно безопасно. Логические ошибки возможны, если привести в неправильный тип или вообще пропустить приведение, когда оно требуется. Не исключено, что после преобразования появится временный объект, который будет благополучно уничтожен вместе со всеми изменениями (большинство компиляторов на это выдают предупреждение).
// Возвращает процент попаданий.
double hitpercent(const int aHitCount, const int aShotCount)
{
    if (aShotCount==0) return 0.0;
    // Нам нужно дробное деление, а не целочисленное - поэтому переведём делимое и делитель в double
    return static_cast<double>(aHitCount*100) / static_cast<double>(aShotCount);
}
 
string s = static_cast<string>("Hello!"); // аналогично string s = string("Hello!");
string s = (string)"Hello!"; // синтаксис Си тоже работает
string s = static_cast<string>(5); // не компилируется - нет подходящего конструктора

dynamic_cast[править | править вики-текст]

  • Назначение: Проводит преобразование типа, предварительно убедившись (с помощью RTTI), что объект expression_from в действительности является объектом типа type_to. Если нет: для указателей возвращает NULL, для ссылок устанавливает аварийную ситуацию std::bad_cast.
  • Ограничения на expression_from: выражение должно быть ссылкой или указателем на объект с хотя бы одной виртуальной функцией.
  • Ограничения на type_to: ссылка или указатель на дочерний по отношению к expression_from тип.
  • Производит ли код: да.
  • Возможные ошибки: относительно безопасно. Логические ошибки возможны, если подать аргумент, не имеющий тип type_to, в то время как код не приспособлен к этому.

const_cast[править | править вики-текст]

  • Назначение: Снятие/установка модификатора const или volatile.
  • Ограничения на expression_from: ссылка или указатель.
  • Ограничения на type_to: должен совпадать с типом expression_from с точностью до модификаторов const или volatile.
  • Производит ли код: нет.
  • Возможные ошибки: чревато попыткой изменить неизменяемый объект.
namespace
{
   // Каждая загрузка DLL создаёт новый сегмент данных.
   // Так что с такими глобальными переменными одна программа не будет
   // мешать другой.
   string s = "Wikipedia";
}
 
typedef char* PChar;
 
// Функция экспортируется DLL'ем и возвращает какую-то строку
// в виде char*. Проблема в том, что std::string::c_str() возвращает
// const char*.
void __declspec(dllexport) WINAPI SomeDllFunction(PChar& rMessage)
{
    rMessage = const_cast<char*>(s.c_str());
}

reinterpret_cast[править | править вики-текст]

  • Назначение: Участок памяти рассматривается как объект другого типа.
  • Ограничения на expression_from: порядковый тип (логический, символьный, целый, перечисляемый), указатель, ссылка.
  • Ограничения на type_to: для порядкового типа или указателя — порядковый тип или указатель. Для ссылки — ссылка.
  • Производит ли код: нет.
  • Возможные ошибки: Участок в реальности может и не иметь этого типа. Нет никакой возможности проверить это, всю ответственность за корректность преобразования программист берёт на себя.
// Возвращает true, если число конечное, и false - если бесконечное или NaN.
bool isfinite(const double x)
{
    const uint64_t &y = reinterpret_cast<const uint64_t&>(x);
    return ((y & UINT64_C(0x7FF0000000000000)) != UINT64_C(0x7FF0000000000000));
}
 
// Ошибка - выражение x+5.0 не является ссылкой.
const long& y = reinterpret_cast<const long&>(x+5.0);

См. также[править | править вики-текст]