跳至内容

Typecasting

类型转换

数值类型的转换

经常需要将一个数值类型转换为另一个数值类型。并非所有数值类型都可以进行转换。以下是允许的类型转换方案:

可能的类型转换方案

带箭头的实线表示几乎不会丢失信息地进行转换。可以使用bool类型代替char类型(两者都占用1字节的内存),使用color类型代替int类型(占用4字节),使用datetime代替long类型(占用8字节)。四条带箭头的灰色线表示可能发生精度损失的转换。例如,整数123456789的位数([int](../integer-types/char-short-int-and-long-types/#int))比[float](../real-types-double-float/)能表示的位数多。

int n=123456789;
   float f=n;    // the content of f is equal to 1.234567892E8
   Print("n = ",n,"   f = ",f);
   // result n= 123456789    f= 123456792.00000

转换为float的数字具有相同的小数位数,但精度较低。与黑色箭头不同,转换可能会丢失数据。char和uchar、short和ushort、int和uint、long和ulong之间的转换可能会导致数据丢失。

将浮点值转换为整数类型时,小数部分总是会被删除。如果要将float四舍五入到最接近的整数(在许多情况下更有用),应使用MathRound()

示例:

//--- Gravitational acceleration
   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);
/*
   Result:
   round_g = 9
   math_round_g = 10
*/

如果两个值通过二进制运算符组合,在执行操作之前,较低类型的操作数会根据以下方案中的优先级转换为较高类型:

  • 由二进制操作进行的转换 char、uchar、short和ushort类型无条件地转换为int类型。

示例:

char   c1=3;
//--- First example
   double d2=c1/2+0.3;
   Print("c1/2 + 0.3 = ",d2);
// Result:   c1/2+0.3 = 1.3

//--- Second example
   d2=c1/2.0+0.3;
   Print("c1/2.0 + 0.3 = ",d2);
// Result:   c1/2.0+0.3 = 1.8

计算表达式包含两个操作。在第一个示例中,char类型的变量c1被转换为int类型的临时变量,因为除法操作中的第二个操作数2是int类型。整数除法3/2的结果是1,属于int类型。

在第一个示例的第二个操作中,第二个操作数是0.3,属于double类型,因此第一个操作的结果被转换为double类型的临时变量1.0。

在第二个示例中,char类型的变量c1被转换为double类型的临时变量,因为除法操作中的第二个操作数2.0是double类型;不再进行其他转换。

数值类型的类型转换

在MQL4语言的表达式中,可以使用显式和隐式类型转换。显式类型转换的格式如下:

var_1 = (type)var_2;

表达式或函数执行结果可以用作var_2变量。显式类型转换的函数风格表示法也是可能的:

var_1 = type(var_2);

让我们考虑基于第一个示例的显式类型转换。

//--- Third example
   double d2=(double)c1/2+0.3;
   Print("(double)c1/2 + 0.3 = ",d2);
// Result:   (double)c1/2+0.3 = 1.80000000

在执行除法操作之前,c1变量被显式转换为double类型。现在整数常量2被转换为double类型的2.0,因为第一个操作数在转换后变成了double类型。实际上,显式类型转换是一元操作。

此外,在尝试转换类型时,结果可能超出允许的范围。在这种情况下,会发生截断。例如:

char c;
   uchar u;
   c=400;
   u=400;
   Print("c = ",c); // Result c=-112
   Print("u = ",u); // Result u=144

在执行操作之前(赋值操作除外),数据会被转换为最高优先级的类型。在执行赋值操作之前,数据会被转换为目标类型。

示例:

int    i=1/2;        // no types casting, the result is 0
   Print("i = 1/2  ",i);

   int k=1/2.0;         // the expression is cast to the double type,
   Print("k = 1/2  ",k);  // then is to the target type of int, the result is 0

   double d=1.0/2.0;    // no types casting, the result is 0.5
   Print("d = 1/2.0; ",d);

   double e=1/2.0;      // the expression is cast to the double type,
   Print("e = 1/2.0; ",e);// that is the same as the target type, the result is 0.5

   double x=1/2;        // the expression of the int type is cast to the double target typr,
   Print("x = 1/2; ",x);  // the result is 0.0

当将long/ulong类型转换为double时,如果整数值大于9223372036854774784或小于-9223372036854774784,可能会丢失精度。

void OnStart()
  {
   long l_max=LONG_MAX;
   long l_min=LONG_MIN+1;
//--- define the highest integer value, which does not lose accuracy when being cast to double
   while(l_max!=long((double)l_max))
      l_max--;
//--- define the lowest integer value, which does not lose accuracy when being cast to double
   while(l_min!=long((double)l_min))
      l_min++;
//--- derive the found interval for integer values
   PrintFormat("When casting an integer value to double, it must be "
               "within [%I64d, %I64d] interval",l_min,l_max);
//--- now, let's see what happens if the value falls out of this interval
   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)));
//--- receive the following result
// When casting an integer value to double, it should be within [-9223372036854774784, 9223372036854774784] interval
// 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 s1=1.0/8;            // the expression is cast to the double type,
   Print("s1 = 1.0/8; ",s1);     //  then is to the target type of string,
// result is "0.12500000" (a string containing 10 characters)

   string s2=NULL;             // string deinitialization
   Print("s2 = NULL; ",s2);      // the result is an empty string
   string s3="Ticket N"+12345; // the expression is cast to the string type
   Print("s3 = \"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));

简单结构类型的类型转换

只有当两个结构的所有成员都是数值类型时,才能相互赋值简单结构类型的数据。在这种情况下,赋值操作的两个操作数(左右)必须是结构类型。不会进行逐成员转换,而是进行简单复制。如果结构的大小不同,则复制较小大小的字节数。这样,MQL4中未使用联合体得到了补偿。

示例:

struct str1
  {
   double  d;
  };
//---
struct str2
  {
   long    l;
  };
//---
struct str3
  {
   int     low_part;
   int     high_part;
  };
//---
struct str4
  {
   string  s;
  };
//+------------------------------------------------------------------+
void OnStart()
  {
   str1 s1;
   str2 s2;
   str3 s3;
   str4 s4;
//---
   s1.d=MathArcsin(2.0);        // get the invalid number -1. # IND
   s2=s1;
   printf("1.  %f    %I64X",s1.d,s2.l);
//---
   s3=s2;
   printf("2.  high part of long %.8X   low part of long %.8X",
          s3.high_part,s3.low_part);
//---
   s4.s="some constant string";
   s3=s4;
   printf("3.  buffer len is %d   constant string address is 0x%.8X",
          s3.low_part,s3.high_part);
  }

另一个示例说明了如何组织一个自定义函数,以接收color类型的RGB(红、绿、蓝)表示形式。创建两个大小相同但内容不同的结构。为了方便,让我们添加一个返回颜色RGB表示的字符串的函数。

#property script_show_inputs
input color          testColor=clrBlue;// set color for testing
//--- structure for representing color as RGB
struct RGB
  {
   uchar             blue;          // blue component of color
   uchar             green;         // green component of color
   uchar             red;           // red component of color
   uchar             empty;         // this byte is not used
   string            toString();    // function for receiving a string
  };
//--- function for showing color as a string
string RGB::toString(void)
  {
   string out="("+(string)red+":"+(string)green+":"+(string)blue+")";
   return out;
  }
//--- structure for storing of the built-in color type
struct builtColor
  {
   color             c;
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- a variable for storing in RGB
   RGB colorRGB;
//--- variable for storing the color type
   builtColor test;
   test.c=testColor;
//--- casting two structures by copying contents
   colorRGB=test;
   Print("color ",test.c,"=",colorRGB.toString());
//---
  }

基类指针到派生类指针的类型转换

开放生成类的对象也可以被视为相应基类的对象。这带来了一些有趣的后果。例如,尽管由同一个基类生成的不同类对象可能彼此有很大差异,我们可以创建它们的链表(List),因为它们被视为基类型对象。但反之则不成立:基类对象不是派生类的自动对象。

可以使用显式类型转换将基类指针转换为派生类的指针。但必须完全确信这种转换是可行的,否则将发生严重的运行时错误,mql4程序将被停止。

使用dynamic_cast运算符进行动态类型转换

动态类型转换是使用只能应用于类指针的dynamic_cast运算符进行的。类型验证在运行时进行。这意味着当使用dynamic_cast运算符时,编译器不会检查用于类型转换的数据类型。如果指针被转换为不是对象实际类型的类型,结果将是NULL

dynamic_cast <type-id> ( expression )

尖括号中的type-id参数应指向先前定义的类类型。与C++不同,表达式操作数的类型可以是任何值,除了void

示例:

class CBar { };
class CFoo : public CBar { };

void OnStart()
  {
   CBar bar;
//--- dynamic casting of *bar pointer type to *foo pointer is allowed
   CFoo *foo = dynamic_cast<CFoo *>(&bar); // no critical error
   Print(foo);                             // foo=NULL
//--- an attempt to explicitly cast a Bar type object reference to a Foo type object is forbidden
   foo=(CFoo *)&bar;                       // critical runtime error
   Print(foo);                             // this string is not executed
  }

另请参阅

数据类型

最后更新于