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

Преобразование числовых типов

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

Схема допустимых преобразований типов

Сплошные линии со стрелками обозначают преобразования, которые выполняются без потери информации. Вместо типа char может выступать тип bool (оба занимают в памяти 1 байт), вместо типа int можно использовать тип color (по 4 байта), а вместо типа long допустим тип datetime (занимают по 8 байт). Четыре штриховые линии серого цвета, также со стрелками, означают преобразования, при которых может произойти потеря точности. Например, количество цифр в целом числе 123456789 (int) превышает количество цифр, которое может быть представлено типом float.

   int n=123456789;
   float f=n;    // содержимое f равно 1.234567892E8
   Print("n = ",n,"   f = ",f);
   // результат n= 123456789    f= 123456792.00000

Число, преобразованное в тип float, имеет тот же порядок, но несколько меньшую точность. Преобразования, обратные черным стрелкам, осуществляется с возможной потерей информацией. Преобразования между char и uchar, short и ushort, int и uint, long и ulong (имеются ввиду преобразования в обе стороны), могут привести к потере информации.

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

Пример:

//--- ускорение свободного падения
   double g=9.8;
   double round_g=(int)g;
   double math_round_g=MathRound(g);
   Print("round_g = ",round_g);
   Print("math_round_g =",math_round_g);
/*
   Результат:
   round_g = 9
   math_round_g = 10
*/

Если два значения объединяются бинарным оператором, то перед выполнением операции операнд младшего типа преобразовывается к более старшему типу в соответствии с приоритетом, указанным на схеме:

Преобразования при объединении бинарной операцией

Типы данных char, uchar, short и ushort в операциях безусловно приводятся к типу int.

Примеры:

   char   c1=3;
//--- первый пример
   double d2=c1/2+0.3;
   Print("c1/2+0.3 = ",d2);
// Результат:   c1/2+0.3 = 1.3
 
//--- второй пример
   d2=c1/2.0+0.3;
   Print("c1/2.0+0.3 = ",d2);
// Результат:   c1/2.0+0.3 = 1.8

Вычисляемое выражение состоит из двух операций. В первом примере переменная c1 типа char преобразуется ко временной переменной типа int, так как второй операнд в операции деления, константа 2, имеет более старший тип int. В результате целочисленного деления 3/2 получается значение 1, которое имеет тип int.

Во второй операции первого примера вторым операндом выступает константа 0.3, которая имеет тип double, поэтому результат первой операции преобразуется во временную переменную типа double со значением 1.0.

Во втором примере переменная c1 типа char преобразуется ко временной переменной типа double, так как второй операнд в операции деления, константа 2.0, имеет тип double; дальнейших преобразований не производится.

 

Приведение числовых типов

В выражениях языка MQL5 можно использовать как явное, так и неявное приведение типов. Явное преобразование типов записывается следующим образом:

var_1 = (тип)var_2;

В качестве переменной var_2 может быть выражение или результат выполнения функции. Допускается также функциональная запись явного приведения типов:

var_1 = тип(var_2);

Рассмотрим явное преобразование на основании первого примера.

//--- третий пример
   double d2=(double)c1/2+0.3;
   Print("(double)c1/2+0.3 = ",d2);
// Результат:   (double)c1/2+0.3 = 1.80000000

Перед выполнением операции деления переменная c1 явно приводится к типу double. Теперь уже целочисленная константа 2 приводится к значению 2.0 типа double, так как в результате преобразования первый операнд получил тип double. Фактически, явное преобразование типов является одноместной операцией.

Кроме того, при попытке приведения типов результат может выйти за пределы допустимого диапазона. В этом случае произойдет усечение. Например:

   char c;
   uchar u;
   c=400;
   u=400;
   Print("c = ",c); // результат с=-112
   Print("u = ",u); // результат u=144

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

Примеры:

   int    i=1/2;          // приведения типов нет, результат: 0
   Print("i = 1/2  ",i);
 
   int k=1/2.0;           // выражение приводится к типу double,
   Print("k = 1/2  ",k);  // затем приводится к целевому типу int, результат: 0
 
   double d=1.0/2.0;      // приведения типов нет, результат: 0.5
   Print("d = 1/2.0; ",d);
 
   double e=1/2.0;        // выражение приводится к типу double, 
   Print("e = 1/2.0; ",e);// который совпадает с целевым типом, результат: 0.5
 
   double x=1/2;          // выражение типа int приводится к целевому типу double,
   Print("x = 1/2; ",x);  // результат: 0.0

При преобразовании типа long/ulong в double может произойти потеря точности: если целое больше 9223372036854774784 или меньше -9223372036854774784.

void OnStart()
  {
   long l_max=LONG_MAX;
   long l_min=LONG_MIN+1;
//--- найдем максимальное целое, которое не теряет точности при приведении к double
   while(l_max!=long((double)l_max))
      l_max--;
//--- найдем минимальное целое, которое не теряет точности при приведении к double
   while(l_min!=long((double)l_min))
      l_min++;
//--- теперь выведем найденный интервал для целых чисел   
   PrintFormat("При приведении целого числа к double оно должно "
               "быть в интервале [%I64d, %I64d]",l_min,l_max);
//--- теперь посмотрим, что произойдет, если число выходит за этот интервал
   PrintFormat("l_max+1=%I64d, double(l_max+1)=%.f, ulong(double(l_max+1))=%I64d",
               l_max+1,double(l_max+1),long(double(l_max+1)));
   PrintFormat("l_min-1=%I64d, double(l_min-1)=%.f, ulong(double(l_min-1))=%I64d",
               l_min-1,double(l_min-1),long(double(l_min-1)));
//--- получим такой вывод
// При приведении целого числа к double оно должно быть в интервале [-9223372036854774784, 9223372036854774784]
// l_max+1=9223372036854774785, double(l_max+1)=9223372036854774800, ulong(double(l_max+1))=9223372036854774784
// l_min-1=-9223372036854774785, double(l_min-1)=-9223372036854774800, ulong(double(l_min-1))=-9223372036854774784
  }

 

Приведения для типа string

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

Примеры:

   string s1=1.0/8;              // выражение приводится к типу double, 
   Print("s1 = 1.0/8; ",s1);     // затем к целевому типу string, 
// результат:"0.12500000"(строка, содержащая 10 символов)
 
   string s2=NULL;               // деинициализация строки
   Print("s2 = NULL; ",s2);      // результат: пустая строка
   string s3="Ticket N"+12345;   // выражение приводится к типу string 
   Print("s = \"Ticket N\"+12345 ",s3);
 
   string str1="true";
   string str2="0,255,0";
   string str3="2009.06.01";
   string str4="1.2345e2";
   Print(bool(str1));
   Print(color(str2));
   Print(datetime(str3));
   Print(double(str4));

 

Приведение типов указателей базовых классов к указателям производных классов

Объекты открыто порожденного класса могут также рассматриваться как объекты соответствующего ему базового класса. Это ведет к некоторым интересным следствиям. Например, вопреки тому факту, что объекты различных классов, порожденных одним базовым классом, могут существенно отличаться друг от друга, мы можем создать их связанный список (List), поскольку мы рассматриваем их как объекты базового типа. Но обратное неверно: объекты базового класса не являются автоматически объектами производного класса.

Можно использовать явное приведение типов для преобразования указателей базового класса в указатели производного класса. Но необходимо быть полностью уверенным в допустимости такого преобразования, так как в противном случае возникнет критическая ошибка времени выполнения и mql5-программа будет остановлена.

Динамическое приведение типов  с помощью оператора dynamic_cast #

Существует возможность динамического приведения типов с помощью оператора dynamic_cast, который может быть применён только к указателям классов. При этом проверка корректности типов производится в момент выполнения программы. Это означает, что при использовании оператора dynamic_cast компилятор не производит проверку типа данных, используемого для приведения. В случае, если осуществляется преобразование указателя к типу данных, который не является фактическим типом объекта, результатом  будет  значение NULL.

dynamic_cast <type-id> ( expression )

Параметр type-id в угловых скобках должен быть указателем на ранее определённый тип класса. Тип операнда expression (в отличии от C++) может быть любым, кроме void.

Пример:

class CBar { };
class CFoo : public CBar { };
 
void OnStart()
  {
   CBar bar;    
//--- динамическое приведение типа указателя *bar к указателю *foo разрешено 
   CFoo *foo = dynamic_cast<CFoo *>(&bar); // критической ошибки выполнения не возникнет   
   Print(foo);                             // foo=NULL      
//--- попытка явного приведения ссылки объекта типа Bar к объекту типа Foo запрещено
   foo=(CFoo *)&bar;                       // возникнет критическая ошибка выполнения
   Print(foo);                             // эта строка не будет выполнена
  }

Смотри также

Типы данных