Mẫu hàm
Các hàm nạp chồng thường được sử dụng để thực hiện các thao tác tương tự trên nhiều kiểu dữ liệu khác nhau. ArraySize()
là một ví dụ đơn giản về hàm như vậy trong MQL5. Nó trả về kích thước của bất kỳ loại mảng nào. Trên thực tế, hàm hệ thống này được nạp chồng và toàn bộ việc triển khai của việc nạp chồng đó được ẩn đi đối với các nhà phát triển ứng dụng MQL5:
int ArraySize(
void& array[] // mảng được kiểm tra
);
2
3
Điều này có nghĩa là trình biên dịch ngôn ngữ MQL5 chèn triển khai cần thiết cho mỗi lần gọi hàm này. Ví dụ, đây là cách nó có thể được thực hiện cho các mảng kiểu số nguyên:
int ArraySize(
int& array[] // mảng với các phần tử kiểu int
);
2
3
Hàm ArraySize()
có thể được hiển thị theo cách sau cho mảng kiểu MqlRates để làm việc với báo giá trong định dạng dữ liệu lịch sử:
int ArraySize(
MqlRates& array[] // mảng chứa các giá trị kiểu MqlRates
);
2
3
Do đó, việc sử dụng cùng một hàm để làm việc với các kiểu khác nhau rất tiện lợi. Tuy nhiên, tất cả công việc chuẩn bị trước phải được thực hiện – hàm cần thiết phải được nạp chồng cho tất cả các kiểu dữ liệu mà nó cần hoạt động chính xác.
Có một giải pháp tiện lợi. Nếu các thao tác tương tự cần được thực thi cho mỗi kiểu dữ liệu, có thể sử dụng mẫu hàm. Trong trường hợp này, lập trình viên chỉ cần viết một mô tả mẫu hàm duy nhất. Khi mô tả mẫu theo cách này, chúng ta chỉ nên chỉ định một tham số hình thức thay vì một kiểu dữ liệu cụ thể mà hàm cần làm việc. Trình biên dịch sẽ tự động tạo ra các hàm khác nhau để xử lý phù hợp từng kiểu dựa trên kiểu của các đối số được sử dụng khi gọi hàm.
Định nghĩa mẫu hàm bắt đầu bằng từ khóa template
, theo sau là danh sách các tham số hình thức trong dấu ngoặc nhọn. Mỗi tham số hình thức được bắt đầu bằng từ khóa typename
. Các kiểu tham số hình thức là các kiểu tích hợp hoặc do người dùng định nghĩa. Chúng được sử dụng:
- để chỉ định kiểu của các đối số hàm,
- để chỉ định kiểu của giá trị trả về của hàm,
- để khai báo các biến bên trong định nghĩa hàm.
Số lượng tham số mẫu không được vượt quá tám. Mỗi tham số hình thức trong định nghĩa mẫu phải xuất hiện trong danh sách tham số hàm ít nhất một lần. Mỗi tên của tham số hình thức phải là duy nhất.
Dưới đây là ví dụ về mẫu hàm để tìm giá trị cao nhất trong mảng của bất kỳ kiểu số nào (số nguyên và số thực):
template<typename T>
T ArrayMax(T &arr[])
{
uint size=`ArraySize`(arr);
if(size==0) return(0);
T max=arr[0];
for(uint n=1;n<size;n++)
if(max<arr[n]) max=arr[n];
//---
return(max);
}
2
3
4
5
6
7
8
9
10
11
12
Mẫu này định nghĩa hàm tìm giá trị cao nhất trong mảng được truyền vào và trả về giá trị này dưới dạng kết quả. Hãy nhớ rằng hàm ArrayMaximum()
tích hợp trong MQL5 chỉ trả về chỉ số của giá trị cao nhất, có thể được sử dụng để tìm chính giá trị đó. Ví dụ:
//--- tạo một mảng
double array[];
int size=50;
`ArrayResize`(array,size);
//--- điền giá trị ngẫu nhiên
for(int i=0;i<size;i++)
{
array[i]=`MathRand`();
}
//--- tìm vị trí của giá trị cao nhất trong mảng
int max_position=`ArrayMaximum`(array);
//--- bây giờ, lấy chính giá trị cao nhất trong mảng
double max=array[max_position];
//--- hiển thị giá trị tìm được
`Print`(`"Max value = "`,max);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Như vậy, chúng ta đã thực hiện hai bước để lấy giá trị cao nhất trong mảng. Với mẫu hàm ArrayMax(), chúng ta có thể nhận được kết quả của kiểu cần thiết chỉ bằng cách truyền mảng của kiểu phù hợp vào hàm này. Điều đó có nghĩa là thay vì hai dòng cuối
//--- tìm vị trí của giá trị cao nhất trong mảng
int max_position=`ArrayMaximum`(array);
//--- bây giờ, lấy chính giá trị cao nhất trong mảng
double max=array[max_position];
2
3
4
chúng ta giờ đây có thể sử dụng chỉ một dòng, trong đó kết quả trả về có cùng kiểu với mảng được truyền vào hàm:
//--- tìm giá trị cao nhất
double max=ArrayMax(array);
2
Trong trường hợp này, kiểu của kết quả trả về bởi hàm ArrayMax() sẽ tự động khớp với kiểu của mảng.
Sử dụng từ khóa typename
để lấy kiểu đối số dưới dạng chuỗi nhằm tạo ra các phương thức đa năng làm việc với nhiều kiểu dữ liệu khác nhau. Hãy xem xét một ví dụ cụ thể về hàm trả về kiểu dữ liệu dưới dạng chuỗi:
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void `OnStart`()
{
//---
CTrade trade;
double d_value=M_PI;
int i_value=INT_MAX;
`Print`(`"d_value: type="`,GetTypeName(d_value), `" value="`, d_value);
`Print`(`"i_value: type="`,GetTypeName(i_value), `" value="`, i_value);
`Print`(`"trade: type="`,GetTypeName(trade));
//---
}
//+------------------------------------------------------------------+
//| Kiểu được trả về dưới dạng chuỗi |
//+------------------------------------------------------------------+
template<typename T>
string GetTypeName(const T &t)
{
//--- trả về kiểu dưới dạng chuỗi
return(`typename`(T));
//---
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Mẫu hàm cũng có thể được sử dụng cho các phương thức lớp, ví dụ:
class CFile
{
...
public:
...
template<typename T>
uint WriteStruct(T &data);
};
template<typename T>
uint CFile::WriteStruct(T &data)
{
...
return(`FileWriteStruct`(m_handle,data));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Mẫu hàm không nên được khai báo với các từ khóa export, virtual và #import.
Nạp chồng hàm mẫu
Việc nạp chồng hàm mẫu có thể cần thiết trong một số trường hợp. Ví dụ, chúng ta có một hàm mẫu ghi giá trị của tham số thứ hai vào tham số thứ nhất bằng cách sử dụng ép kiểu. MQL5 không cho phép ép kiểu chuỗi sang bool. Chúng ta có thể tự làm điều đó – hãy tạo một phiên bản nạp chồng của hàm mẫu. Ví dụ:
//+------------------------------------------------------------------+
//| Hàm mẫu |
//+------------------------------------------------------------------+
template<typename T1,typename T2>
string Assign(T1 &var1,T2 var2)
{
var1=(T1)var2;
return(`__FUNCSIG__`);
}
//+------------------------------------------------------------------+
//| Nạp chồng đặc biệt cho bool+string |
//+------------------------------------------------------------------+
string Assign(bool &var1,string var2)
{
var1=(`StringCompare`(var2,`"true"`,`false`) || `StringToInteger`(var2)!=0);
return(`__FUNCSIG__`);
}
//+------------------------------------------------------------------+
//| Hàm khởi động chương trình script |
//+------------------------------------------------------------------+
void `OnStart`()
{
int i;
bool b;
`Print`(Assign(i,`"test"`));
`Print`(Assign(b,`"test"`));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Kết quả của việc thực thi mã, chúng ta có thể thấy rằng hàm mẫu Assign() đã được sử dụng cho cặp int+string, trong khi phiên bản nạp chồng đã được sử dụng cho cặp bool+string trong lần gọi thứ hai.
string Assign<int,string(int&,string)
string Assign(bool&,string)
2
Xem thêm