Ép kiểu
Ép kiểu các kiểu số
Thường xuyên có nhu cầu chuyển đổi một kiểu số sang một kiểu số khác. Không phải tất cả các kiểu số đều có thể chuyển đổi sang kiểu khác. Dưới đây là sơ đồ của các phép ép kiểu được phép:
Các đường liền nét có mũi tên biểu thị các thay đổi được thực hiện hầu như không mất thông tin. Thay vì kiểu char
, có thể sử dụng kiểu bool (cả hai đều chiếm 1 byte bộ nhớ), thay vì kiểu int
, có thể sử dụng kiểu color (4 byte), thay vì kiểu long
, có thể sử dụng datetime (chiếm 8 byte). Bốn đường nét đứt màu xám, cũng có mũi tên, biểu thị các chuyển đổi có thể dẫn đến mất độ chính xác. Ví dụ, số chữ số trong một số nguyên bằng 123456789 (int) cao hơn số chữ số mà float có thể biểu diễn.
int n=123456789;
float f=n; // nội dung của f bằng 1.234567892E8
Print("n = ",n," f = ",f);
// kết quả n= 123456789 f= 123456792.00000
2
3
4
Một số được chuyển đổi sang float
có cùng thứ tự, nhưng ít chính xác hơn. Các chuyển đổi ngược với các mũi tên đen có thể được thực hiện nhưng có thể mất dữ liệu. Chuyển đổi giữa char
và uchar
, short
và ushort
, int
và uint
, long
và ulong
(chuyển đổi hai chiều) có thể dẫn đến mất dữ liệu.
Kết quả của việc chuyển đổi giá trị số thực sang kiểu số nguyên, phần thập phân luôn bị xóa bỏ. Nếu bạn muốn làm tròn một số float
thành số nguyên gần nhất (trong nhiều trường hợp hữu ích hơn), bạn nên sử dụng MathRound().
Ví dụ:
//--- Gia tốc trọng trường
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);
/*
Kết quả:
round_g = 9
math_round_g = 10
*/
2
3
4
5
6
7
8
9
10
11
Nếu hai giá trị được kết hợp bởi một toán tử nhị phân, trước khi thực hiện phép toán, toán hạng có kiểu thấp hơn sẽ được chuyển đổi sang kiểu cao hơn theo thứ tự ưu tiên được đưa ra trong sơ đồ dưới đây:
Các kiểu dữ liệu char
, uchar
, short
, và ushort
được chuyển đổi vô điều kiện sang kiểu int
.
Ví dụ:
char c1=3;
//--- Ví dụ đầu tiên
double d2=c1/2+0.3;
Print("c1/2 + 0.3 = ",d2);
// Kết quả: c1/2+0.3 = 1.3
//--- Ví dụ thứ hai
d2=c1/2.0+0.3;
Print("c1/2.0 + 0.3 = ",d2);
// Kết quả: c1/2.0+0.3 = 1.8
2
3
4
5
6
7
8
9
10
Biểu thức được tính toán bao gồm hai phép toán. Trong ví dụ đầu tiên, biến c1
kiểu char
được chuyển đổi thành một biến tạm thời kiểu int
, vì toán hạng thứ hai trong phép chia, hằng số 2
, thuộc kiểu int
cao hơn. Kết quả của phép chia số nguyên 3/2 là giá trị 1
, thuộc kiểu int
.
Trong phép toán thứ hai của ví dụ đầu tiên, toán hạng thứ hai là hằng số 0.3
, thuộc kiểu double
, nên kết quả của phép toán đầu tiên được chuyển đổi thành một biến tạm thời kiểu double
với giá trị 1.0
.
Trong ví dụ thứ hai, biến kiểu char
c1
được chuyển đổi thành một biến tạm thời kiểu double
, vì toán hạng thứ hai trong phép chia, hằng số 2.0
, thuộc kiểu double
; không có thêm chuyển đổi nào được thực hiện.
Ép kiểu của các kiểu số
Trong các biểu thức của ngôn ngữ MQL5, có thể sử dụng cả ép kiểu rõ ràng và ngầm định. Ép kiểu rõ ràng được viết như sau:
var_1 = (type)var_2;
Một biểu thức hoặc kết quả thực thi hàm có thể được sử dụng làm biến var_2
. Ký hiệu kiểu hàm của ép kiểu rõ ràng cũng có thể được sử dụng:
var_1 = type(var_2);
Hãy xem xét một ép kiểu rõ ràng dựa trên ví dụ đầu tiên.
//--- Ví dụ thứ ba
double d2=(double)c1/2+0.3;
Print("(double)c1/2 + 0.3 = ",d2);
// Kết quả: (double)c1/2+0.3 = 1.80000000
2
3
4
Trước khi thực hiện phép chia, biến c1
được ép kiểu rõ ràng sang kiểu double
. Bây giờ hằng số số nguyên 2
được ép thành giá trị 2.0
kiểu double
, vì kết quả của việc chuyển đổi toán hạng đầu tiên đã mang kiểu double
. Thực tế, ép kiểu rõ ràng là một phép toán đơn ngôi.
Ngoài ra, khi cố gắng ép kiểu, kết quả có thể vượt quá phạm vi cho phép. Trong trường hợp này, xảy ra hiện tượng cắt bớt. Ví dụ:
char c;
uchar u;
c=400;
u=400;
Print("c = ",c); // Kết quả c=-112
Print("u = ",u); // Kết quả u=144
2
3
4
5
6
Trước khi thực hiện các phép toán (ngoại trừ phép gán), dữ liệu được chuyển đổi thành kiểu ưu tiên tối đa. Trước khi thực hiện các phép toán gán, dữ liệu được ép thành kiểu mục tiêu.
Ví dụ:
int i=1/2; // không có ép kiểu, kết quả là 0
Print("i = 1/2 ",i);
int k=1/2.0; // biểu thức được ép sang kiểu double,
Print("k = 1/2 ",k); // sau đó sang kiểu mục tiêu int, kết quả là 0
double d=1.0/2.0; // không có ép kiểu, kết quả là 0.5
Print("d = 1/2.0; ",d);
double e=1/2.0; // biểu thức được ép sang kiểu double,
Print("e = 1/2.0; ",e); // giống kiểu mục tiêu, kết quả là 0.5
double x=1/2; // biểu thức kiểu int được ép sang kiểu mục tiêu double,
Print("x = 1/2; ",x); // kết quả là 0.0
2
3
4
5
6
7
8
9
10
11
12
13
14
Khi chuyển đổi kiểu long
/ulong
sang double
, độ chính xác có thể bị mất nếu giá trị số nguyên lớn hơn 9223372036854774784 hoặc nhỏ hơn -9223372036854774784.
void OnStart()
{
long l_max=LONG_MAX;
long l_min=LONG_MIN+1;
//--- xác định giá trị số nguyên cao nhất không mất độ chính xác khi ép sang double
while(l_max!=long((double)l_max))
l_max--;
//--- xác định giá trị số nguyên thấp nhất không mất độ chính xác khi ép sang double
while(l_min!=long((double)l_min))
l_min++;
//--- suy ra khoảng giá trị số nguyên tìm được
PrintFormat("Khi ép giá trị số nguyên sang double, nó phải nằm trong khoảng [%I64d, %I64d]",l_min,l_max);
//--- giờ hãy xem điều gì xảy ra nếu giá trị vượt ra ngoài khoảng này
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)));
//--- nhận được kết quả sau
// Khi ép giá trị số nguyên sang double, nó nên nằm trong khoảng [-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
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Ép kiểu cho kiểu chuỗi
Kiểu string
có ưu tiên cao nhất trong số các kiểu đơn giản. Do đó, nếu một trong các toán hạng của phép toán thuộc kiểu string
, toán hạng thứ hai sẽ tự động được ép sang kiểu string
. Lưu ý rằng đối với một chuỗi, chỉ có thể thực hiện một phép toán cộng hai ngôi duy nhất. Ép kiểu rõ ràng từ string
sang bất kỳ kiểu số nào đều được phép.
Ví dụ:
string s1=1.0/8; // biểu thức được ép sang kiểu double,
Print("s1 = 1.0/8; ",s1); // sau đó sang kiểu mục tiêu string,
// kết quả là "0.12500000" (một chuỗi chứa 10 ký tự)
string s2=NULL; // giải khởi tạo chuỗi
Print("s2 = NULL; ",s2); // kết quả là chuỗi rỗng
string s3="Ticket N"+12345; // biểu thức được ép sang kiểu string
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));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Ép kiểu của con trỏ lớp cơ sở sang con trỏ của lớp dẫn xuất
Các đối tượng của lớp mở rộng cũng có thể được xem như các đối tượng của lớp cơ sở tương ứng. Điều này dẫn đến một số hậu quả thú vị. Ví dụ, mặc dù các đối tượng của các lớp khác nhau, được tạo ra từ một lớp cơ sở duy nhất, có thể khác nhau đáng kể, chúng ta vẫn có thể tạo một danh sách liên kết (List) của chúng, khi xem chúng như các đối tượng của kiểu cơ sở. Nhưng điều ngược lại không đúng: các đối tượng lớp cơ sở không tự động là các đối tượng của lớp dẫn xuất.
Bạn có thể sử dụng ép kiểu rõ ràng để chuyển đổi con trỏ lớp cơ sở sang con trỏ của lớp dẫn xuất. Nhưng bạn phải hoàn toàn chắc chắn về tính hợp lệ của phép biến đổi này, vì nếu không sẽ xảy ra lỗi thời gian chạy nghiêm trọng và chương trình MQL5 sẽ bị dừng.
Ép kiểu động sử dụng toán tử dynamic_cast
Ép kiểu động được thực hiện bằng toán tử dynamic_cast
, chỉ có thể áp dụng cho các con trỏ đến lớp. Việc kiểm tra kiểu được thực hiện tại thời gian chạy. Điều này có nghĩa là trình biên dịch không kiểm tra kiểu dữ liệu được áp dụng cho ép kiểu khi sử dụng toán tử dynamic_cast
. Nếu một con trỏ được chuyển đổi sang kiểu dữ liệu không phải là kiểu thực tế của đối tượng, kết quả là NULL.
dynamic_cast <type-id> ( expression )
Tham số type-id
trong dấu ngoặc nhọn phải trỏ đến một kiểu lớp đã được định nghĩa trước đó. Không giống C++, kiểu toán hạng expression
có thể là bất kỳ giá trị nào trừ void.
Ví dụ:
class CBar { };
class CFoo : public CBar { };
void OnStart()
{
CBar bar;
//--- ép kiểu động của con trỏ *bar sang con trỏ *foo được phép
CFoo *foo = dynamic_cast<CFoo *>(&bar); // không có lỗi nghiêm trọng
Print(foo); // foo=NULL
//--- cố gắng ép kiểu rõ ràng tham chiếu đối tượng kiểu Bar sang đối tượng kiểu Foo bị cấm
foo=(CFoo *)&bar; // lỗi thời gian chạy nghiêm trọng
Print(foo); // dòng này không được thực thi
}
2
3
4
5
6
7
8
9
10
11
12
13
Xem thêm