Lợi ích của mẫu
Các mẫu hàm được sử dụng khi bạn cần thực hiện các thao tác tương tự trên nhiều kiểu dữ liệu khác nhau, ví dụ như tìm kiếm phần tử lớn nhất trong mảng. Lợi thế chính của việc áp dụng mẫu là bạn không phải viết mã nạp chồng riêng cho từng kiểu. Thay vì khai báo nhiều nạp chồng cho mỗi kiểu
double ArrayMax(double array[])
{
...
}
int ArrayMax(int array[])
{
...
}
uint ArrayMax(uint array[])
{
...
}
long ArrayMax(long array[])
{
...
}
datetime ArrayMax(datetime array[])
{
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
chúng ta chỉ cần viết một hàm mẫu duy nhất
template<typename T>
T ArrayMax(T array[])
{
if(`ArraySize`()==0)
return(0);
uint max_index=`ArrayMaximum`(array);
return(array[max_index]);
}
2
3
4
5
6
7
8
để sử dụng trong mã của bạn:
double high[];
datetime time[];
....
double max_high=ArrayMax(high);
datetime lasttime=ArrayMax(time);
2
3
4
5
Ở đây, tham số hình thức T
chỉ định kiểu dữ liệu được sử dụng sẽ được thay thế bằng kiểu thực tế áp dụng trong quá trình biên dịch, tức là trình biên dịch tự động tạo một hàm riêng cho mỗi kiểu – double, datetime, v.v. MQL5 cũng cho phép bạn phát triển các mẫu lớp bằng cách tận dụng tất cả các lợi ích của phương pháp này.
Mẫu lớp
Mẫu lớp được khai báo bằng từ khóa template
theo sau là dấu ngoặc nhọn <> liệt kê danh sách các tham số hình thức với từ khóa typename
. Mục này thông báo cho trình biên dịch rằng nó đang xử lý một lớp tổng quát với tham số hình thức T
xác định kiểu biến thực tế khi triển khai lớp. Ví dụ, hãy tạo một lớp vector để lưu trữ mảng với các phần tử kiểu T
:
#define TOSTR(x) #x+`" "` // macro để hiển thị tên đối tượng
//+------------------------------------------------------------------+
//| Lớp Vector để lưu trữ các phần tử kiểu T |
//+------------------------------------------------------------------+
template <typename T>
class TArray
{
protected:
T m_array[];
public:
//--- hàm tạo mặc định tạo mảng cho 10 phần tử
void TArray(void){`ArrayResize`(m_array,10);}
//--- hàm tạo để tạo vector với kích thước mảng được chỉ định
void TArray(int size){`ArrayResize`(m_array,size);}
//--- trả về kiểu và số lượng dữ liệu lưu trữ trong đối tượng kiểu TArray
string Type(void){return(`typename`(m_array[0])+`":"`+(string)`ArraySize`(m_array));};
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Tiếp theo, hãy áp dụng các phương pháp khác nhau để tạo ba đối tượng TArray
trong chương trình để làm việc với các kiểu khác nhau
void `OnStart`()
{
TArray<double> double_array; // vector có kích thước mặc định là 10
TArray<int> int_array(15); // vector có kích thước 15
TArray<string> *string_array; // con trỏ tới vector TArray<string>
//--- tạo đối tượng động
string_array=new TArray<string>(20);
//--- hiển thị tên đối tượng, kiểu dữ liệu và kích thước vector trong Nhật ký
`PrintFormat`(`"%s (%s)"`,TOSTR(double_array),double_array.Type());
`PrintFormat`(`"%s (%s)"`,TOSTR(int_array),int_array.Type());
`PrintFormat`(`"%s (%s)"`,TOSTR(string_array),string_array.Type());
//--- xóa đối tượng động trước khi hoàn tất chương trình
delete(string_array);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Kết quả thực thi script:
double_array (double:10)
int_array (int:15)
string_array (string:20)
2
3
Bây giờ, chúng ta có 3 vector với các kiểu dữ liệu khác nhau: double, int và string.
Mẫu lớp rất phù hợp để phát triển các container – các đối tượng được thiết kế để bao bọc các đối tượng khác của bất kỳ kiểu nào. Các đối tượng container là tập hợp đã chứa các đối tượng của một kiểu nhất định. Thông thường, việc làm việc với dữ liệu lưu trữ được tích hợp ngay vào container.
Ví dụ, bạn có thể tạo một mẫu lớp không cho phép truy cập phần tử ngoài mảng, từ đó tránh lỗi nghiêm trọng ngoài phạm vi.
//+------------------------------------------------------------------+
//| Lớp cho phép truy cập tự do vào phần tử mảng |
//+------------------------------------------------------------------+
template<typename T>
class TSafeArray
{
protected:
T m_array[];
public:
//--- hàm tạo mặc định
void TSafeArray(void){}
//--- hàm tạo để tạo mảng với kích thước được chỉ định
void TSafeArray(int size){`ArrayResize`(m_array,size);}
//--- kích thước mảng
int Size(void){return(`ArraySize`(m_array));}
//--- thay đổi kích thước mảng
int Resize(int size,int reserve){return(`ArrayResize`(m_array,size,reserve));}
//--- giải phóng mảng
void Erase(void){`ZeroMemory`(m_array);}
//--- toán tử để truy cập phần tử mảng bằng chỉ số
T operator[](int index);
//--- toán tử gán để nhận tất cả phần tử từ mảng cùng lúc
void operator=(const T &array[]); // mảng kiểu T
};
//+------------------------------------------------------------------+
//| Nhận phần tử theo chỉ số |
//+------------------------------------------------------------------+
template<typename T>
T TSafeArray::operator[](int index)
{
static T invalid_value;
//---
int max=`ArraySize`(m_array)-1;
if(index<0 || index>=`ArraySize`(m_array))
{
`PrintFormat`(`"%s index %d is not in range (0-%d)!"`,`__FUNCTION__`,index,max);
return(invalid_value);
}
//---
return(m_array[index]);
}
//+------------------------------------------------------------------+
//| Gán cho mảng |
//+------------------------------------------------------------------+
template<typename T>
void TSafeArray::operator=(const T &array[])
{
int size=`ArraySize`(array);
`ArrayResize`(m_array,size);
//--- kiểu T phải hỗ trợ toán tử sao chép
for(int i=0;i<size;i++)
m_array[i]=array[i];
//---
}
//+------------------------------------------------------------------+
//| Hàm khởi động chương trình script |
//+------------------------------------------------------------------+
void `OnStart`()
{
int copied,size=15;
MqlRates rates[];
//--- sao chép mảng báo giá
if((copied=`CopyRates`(_Symbol,_Period,0,size,rates))!=size)
{
`PrintFormat`(`"CopyRates(%s,%s,0,%d) returned %d error code"`,
_Symbol,`EnumToString`(_Period),size,`GetLastError`());
return;
}
//--- tạo container và chèn mảng giá trị MqlRates vào đó
TSafeArray<MqlRates> safe_rates;
safe_rates=rates;
//--- chỉ số trong mảng
int index=3;
`PrintFormat`(`"Close[%d]=%G"`,index,safe_rates[index].close);
//--- chỉ số ngoài mảng
index=size;
`PrintFormat`(`"Close[%d]=%G"`,index,safe_rates[index].close);
}
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
Vui lòng lưu ý rằng khai báo mẫu cũng nên được sử dụng khi mô tả các phương thức bên ngoài khai báo lớp:
template<typename T>
T TSafeArray::operator[](int index)
{
...
}
template<typename T>
void TSafeArray::operator=(const T &array[])
{
...
}
2
3
4
5
6
7
8
9
10
Mẫu lớp và hàm cho phép bạn định nghĩa nhiều tham số hình thức phân tách bằng dấu phẩy, ví dụ, bộ sưu tập Map để lưu trữ các cặp "khóa – giá trị":
template<typename Key, template Value>
class TMap
{
...
}
2
3
4
5
Xem thêm