Bố trí cấu trúc và kế thừa
Các cấu trúc có thể chứa các cấu trúc khác làm trường của chúng. Ví dụ, hãy định nghĩa cấu trúc Inclosure
và sử dụng kiểu này cho trường data
trong cấu trúc Main
(StructsComposition.mq5
):
struct Inclosure
{
double X, Y;
};
struct Main
{
Inclosure data;
int code;
};
void OnStart()
{
Main m = {{0.1, 0.2}, -1}; // khởi tạo tổng hợp
m.data.X = 1.0; // gán từng phần tử
m.data.Y = -1.0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Trong danh sách khởi tạo, trường data
được biểu diễn bằng một cấp dấu ngoặc nhọn bổ sung chứa các giá trị trường của Inclosure
. Để truy cập các trường của cấu trúc như vậy, bạn cần sử dụng hai phép toán tham chiếu.
Nếu cấu trúc lồng không được sử dụng ở bất kỳ đâu khác, nó có thể được khai báo trực tiếp bên trong cấu trúc bên ngoài.
struct Main2
{
struct Inclosure2
{
double X, Y;
}
data;
int code;
};
2
3
4
5
6
7
8
9
Một cách khác để bố trí cấu trúc là kế thừa. Cơ chế này thường được sử dụng để xây dựng hệ thống phân cấp lớp (và sẽ được thảo luận chi tiết trong phần tương ứng), nhưng nó cũng khả dụng cho các cấu trúc.
Khi định nghĩa một kiểu cấu trúc mới, lập trình viên có thể chỉ định kiểu của cấu trúc cha trong tiêu đề của nó, sau dấu hai chấm (cấu trúc cha phải được định nghĩa trước đó trong mã nguồn). Kết quả là, tất cả các trường của cấu trúc cha sẽ được thêm vào cấu trúc con (ở phần đầu của nó), và các trường riêng của cấu trúc mới sẽ được đặt trong bộ nhớ sau các trường của cấu trúc cha.
struct Main3 : Inclosure
{
int code;
};
2
3
4
Cấu trúc cha ở đây không phải là cấu trúc lồng, mà là một phần không thể tách rời của cấu trúc con. Vì điều này, việc điền giá trị cho các trường không yêu cầu dấu ngoặc nhọn bổ sung khi khởi tạo, hoặc một chuỗi nhiều toán tử tham chiếu.
Main3 m3 = {0.1, 0.2, -1};
m3.X = 1.0;
m3.Y = -1.0;
2
3
Cả ba cấu trúc được xem xét là Main
, Main2
, và Main3
có cùng biểu diễn bộ nhớ và kích thước 20 byte. Nhưng chúng là các kiểu khác nhau.
Print(sizeof(Main)); // 20
Print(sizeof(Main2)); // 20
Print(sizeof(Main3)); // 20
2
3
Như đã nói trước đây (xem Sao chép cấu trúc), toán tử gán '=', có thể được sử dụng để sao chép các kiểu cấu trúc liên quan, cụ thể là những cấu trúc được liên kết bởi một chuỗi kế thừa. Nói cách khác, một cấu trúc của kiểu cha có thể được ghi vào một cấu trúc của kiểu con (trong trường hợp này, các trường được thêm vào trong cấu trúc dẫn xuất sẽ không bị thay đổi), hoặc ngược lại, một cấu trúc kiểu con có thể được ghi vào một cấu trúc kiểu cha (trong trường hợp này, các trường "thừa" sẽ bị cắt bỏ).
Ví dụ:
Inclosure in = {10, 100};
m3 = in;
2
Ở đây, biến m3
có kiểu Main3
được kế thừa từ Inclosure
. Kết quả của phép gán m3 = in
, các trường X
và Y
(phần chung cho cả hai kiểu) sẽ được sao chép từ biến in
của kiểu cơ sở vào các trường X
và Y
trong biến m3
của kiểu dẫn xuất. Trường code
của biến m3
sẽ không thay đổi.
Không quan trọng liệu cấu trúc con là hậu duệ trực tiếp của tổ tiên hay là một hậu duệ xa, tức là chuỗi kế thừa có thể dài. Việc sao chép các trường chung như vậy hoạt động giữa "con", "cháu" và các kết hợp khác của các kiểu từ các nhánh khác nhau của "cây gia đình".
Nếu cấu trúc cha chỉ có các hàm tạo với tham số, nó phải được gọi từ danh sách khởi tạo khi hàm tạo của cấu trúc dẫn xuất được kế thừa. Ví dụ:
struct Base
{
const int mode;
string s;
Base(const int m) : mode(m) { }
};
struct Derived : Base
{
double data[10];
// nếu chúng ta xóa hàm tạo, sẽ xảy ra lỗi:
Derived() : Base(1) { } // 'Base' - số lượng tham số không đúng
};
2
3
4
5
6
7
8
9
10
11
12
13
Trong hàm tạo Base
, chúng ta điền giá trị cho trường mode
. Vì nó có bộ điều chỉnh const
, hàm tạo là cách duy nhất để đặt giá trị cho nó, và điều này phải được thực hiện dưới dạng cú pháp khởi tạo đặc biệt sau dấu hai chấm (bạn không thể gán một hằng số trong thân hàm tạo nữa). Việc có một hàm tạo rõ ràng khiến trình biên dịch không tạo ra một hàm tạo ngầm định (không có tham số). Tuy nhiên, chúng ta không có hàm tạo không tham số rõ ràng trong cấu trúc Base
, và khi không có nó, bất kỳ lớp dẫn xuất nào cũng không biết cách gọi đúng hàm tạo Base
với một tham số. Do đó, trong cấu trúc Derived
, cần phải khởi tạo rõ ràng hàm tạo cơ sở: điều này cũng được thực hiện bằng cú pháp khởi tạo trong tiêu đề hàm tạo, sau dấu ':', trong trường hợp này, chúng ta gọi Base(1)
.
Nếu chúng ta xóa hàm tạo Derived
, chúng ta sẽ nhận được lỗi "số lượng tham số không hợp lệ" trong hàm tạo cơ sở, vì trình biên dịch cố gắng gọi hàm tạo cho Base
theo mặc định (mà lẽ ra phải có 0 tham số).
Chúng ta sẽ đề cập đến cú pháp và cơ chế kế thừa chi tiết hơn trong Chương về Lớp.