Định nghĩa lớp
Câu lệnh định nghĩa lớp có nhiều thành phần tùy chọn ảnh hưởng đến đặc điểm của nó. Ở dạng tổng quát, nó có thể được biểu diễn như sau:
class class_name [: modifier_access name_parent_class ...]
{
[ modifier_access:]
[description_member...]
...
};
2
3
4
5
6
Để dễ trình bày hơn, chúng ta sẽ bắt đầu với cú pháp tối thiểu đủ dùng và sẽ mở rộng nó khi đi sâu vào tài liệu.
Điểm xuất phát của chúng ta là một nhiệm vụ với một chương trình vẽ giả định hỗ trợ nhiều loại hình dạng.
Để định nghĩa một lớp mới, sử dụng từ khóa class
, theo sau là định danh của lớp và một khối mã trong dấu ngoặc nhọn. Như tất cả các câu lệnh, định nghĩa này phải kết thúc bằng dấu chấm phẩy.
Khối mã có thể để trống. Ví dụ, một mẫu có thể biên dịch của lớp Shape
cho chương trình vẽ trông như sau:
class Shape
{
};
2
3
Từ các chương trước của cuốn sách, chúng ta biết rằng dấu ngoặc nhọn biểu thị ngữ cảnh hoặc phạm vi của các biến. Khi các khối như vậy xuất hiện trong định nghĩa hàm, chúng xác định ngữ cảnh cục bộ của nó. Ngoài ra, còn có ngữ cảnh toàn cục nơi các hàm và biến toàn cục được định nghĩa.
Lần này, dấu ngoặc trong định nghĩa lớp xác định một loại ngữ cảnh mới, ngữ cảnh lớp. Nó là một bộ chứa cho cả biến và hàm được khai báo bên trong lớp.
Việc mô tả các biến để lưu trữ thuộc tính của lớp được thực hiện bằng các câu lệnh thông thường bên trong khối (Shapes1.mq5
).
class Shape
{
int x, y; // tọa độ tâm
color backgroundColor; // màu nền
};
2
3
4
5
Ở đây, chúng ta đã khai báo một số trường đã được thảo luận trong các phần lý thuyết: tọa độ tâm của hình dạng và màu nền.
Sau khi mô tả như vậy, kiểu do người dùng định nghĩa Shape
trở nên khả dụng trong chương trình cùng với các kiểu tích hợp sẵn. Đặc biệt, chúng ta có thể tạo một biến thuộc kiểu này, và nó sẽ chứa các trường đã chỉ định bên trong. Tuy nhiên, chúng ta chưa thể làm gì với chúng và thậm chí không thể chắc chắn rằng chúng có tồn tại.
void OnStart()
{
Shape s;
// lỗi: không thể truy cập thành viên private được khai báo trong lớp 'Shape'
Print(s.x, " ", s.y);
}
2
3
4
5
6
Các thành viên của lớp mặc định là private
, và do đó không thể truy cập từ các phần khác của mã bên ngoài lớp. Đây là nguyên tắc đóng gói đang hoạt động.
Nếu chúng ta cố gắng xuất một hình dạng ra nhật ký, kết quả sẽ khiến chúng ta thất vọng vì nhiều lý do.
Cách tiếp cận đơn giản nhất sẽ gây ra lỗi "các đối tượng chỉ được truyền bằng tham chiếu" (chúng ta cũng đã thấy điều này với cấu trúc):
Print(s); // 's' - các đối tượng chỉ được truyền bằng tham chiếu
Các đối tượng có thể bao gồm nhiều trường, và vì kích thước lớn của chúng, việc truyền chúng theo giá trị là không hiệu quả. Do đó, trình biên dịch yêu cầu các tham số kiểu đối tượng phải được truyền bằng tham chiếu, trong khi Print
nhận giá trị.
Từ phần về tham số hàm (xem phần Tham số giá trị và tham số tham chiếu), chúng ta biết rằng ký hiệu '&' được sử dụng để mô tả tham chiếu. Sẽ hợp lý khi giả định rằng để lấy một tham chiếu đến một biến (trong trường hợp này, một đối tượng s
thuộc kiểu Shape
), cần đặt ký hiệu này trước tên của nó.
Print(&s);
Câu lệnh này biên dịch và chạy mà không có vấn đề gì nhưng không thực sự làm điều chúng ta mong đợi.
Chương trình xuất ra một số nguyên trong quá trình thực thi, ví dụ, 1 hoặc 2097152 (rất có thể sẽ khác nhau). Dấu & trước tên biến có nghĩa là lấy một con trỏ đến biến này, không phải tham chiếu (trái ngược với mô tả tham số hàm).
Con trỏ sẽ được thảo luận chi tiết trong một phần riêng. Tuy nhiên, lưu ý rằng MQL5 không cung cấp quyền truy cập trực tiếp vào bộ nhớ, và con trỏ đến một đối tượng là một bộ mô tả, hay nói đơn giản, một số định danh duy nhất của đối tượng (được gán bởi chính terminal). Nhưng ngay cả khi con trỏ trỏ đến một địa chỉ trong bộ nhớ (như trong C++), điều đó cũng không cung cấp cách hợp pháp để đọc nội dung của đối tượng.
Để xuất nội dung của các đối tượng Shape
ra nhật ký hoặc bất cứ đâu, cần có một hàm thành viên của lớp. Hãy gọi nó là toString
: nó nên trả về một chuỗi với một số mô tả của đối tượng. Chúng ta có thể quyết định sau xem nên hiển thị gì trong đó. Hãy cũng dành sẵn phương thức draw
để vẽ hình dạng. Hiện tại, nó sẽ đóng vai trò như một khai báo của giao diện lập trình đối tượng trong tương lai.
class Shape
{
int x, y; // tọa độ tâm
color backgroundColor; // màu nền
string toString()
{
...
}
void draw() { /* phần giữ chỗ giao diện vẽ trong tương lai */ }
};
2
3
4
5
6
7
8
9
10
11
12
Việc định nghĩa các hàm phương thức được thực hiện theo cách thông thường, với sự khác biệt duy nhất là chúng nằm bên trong khối mã tạo thành lớp.
Trong tương lai, chúng ta sẽ học cách tách khai báo của một hàm bên trong khối lớp và định nghĩa của nó bên ngoài khối. Cách tiếp cận này thường được sử dụng để đặt các khai báo trong một tệp tiêu đề và "ẩn" các định nghĩa trong một tệp mq5. Điều này làm cho mã dễ hiểu hơn (do giao diện lập trình được trình bày riêng biệt, dưới dạng gọn gàng, không có triển khai). Nó cũng cho phép thư viện phần mềm được phân phối dưới dạng tệp ex5 nếu cần (không có mã nguồn chính nhưng cung cấp một tệp tiêu đề đủ để gọi các phương thức giao diện bên ngoài).
Vì phương thức toString
là một phần của lớp, nó có quyền truy cập vào các biến và có thể chuyển đổi chúng thành chuỗi. Ví dụ,
string toString()
{
return (string)x + " " + (string)y;
}
2
3
4
Tuy nhiên, hiện tại toString
và draw
là private
, cũng như các trường còn lại. Chúng ta cần làm cho chúng có thể truy cập từ bên ngoài lớp.