Class
Lớp (class) là mở rộng của struct, không chỉ có dữ liệu thành viên mà còn có hàm thành viên.
Trong lập trình hướng đối tượng (OOP), đối tượng là thể hiện của lớp, tức là biến.
Trong C++, struct cũng định nghĩa lớp; khái niệm struct ở phần trước là từ C. Vì lý do lịch sử, C++ giữ và mở rộng struct.
Định nghĩa lớp
Lớp dùng class hoặc struct, dưới đây dùng class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Tương tự struct. Ví dụ trên định nghĩa lớp Object với hai thành viên weight,value, và sau } khai báo mảng e.
Con trỏ tới lớp giống struct.
Bộ chỉ định truy cập
Khác với ví dụ struct, ở đây có public, đây là bộ chỉ định truy cập.
public: các thành viên sau đó đều truy cập được từ trong lớp và ngoài lớp.protected: truy cập được từ trong lớp, lớp dẫn xuất hoặc friend, nhưng không từ ngoài lớp.private: chỉ truy cập từ trong lớp hoặc friend; không từ ngoài lớp hoặc lớp dẫn xuất.
Với struct, mặc định là public; với class, mặc định là private.
Khái niệm cơ bản về friend và lớp dẫn xuất
Friend (friend): đánh dấu một hàm hoặc lớp để có thể truy cập private/protected của lớp đó, dù không là thành viên. Nói đơn giản, có friend thì truy cập được phần riêng tư.
Lớp dẫn xuất (derived class): C++ cho phép dùng một lớp làm lớp cơ sở và tạo lớp dẫn xuất. Lớp dẫn xuất kế thừa thành viên và hàm theo quy tắc. Tăng tái sử dụng.
Lớp dẫn xuất giống quan hệ "is". Ví dụ mèo (lớp dẫn xuất) "is" động vật có vú (lớp cơ sở).
Khác biệt private và protected: lớp dẫn xuất truy cập được protected (cũng như public) nhưng không truy cập private.
Truy cập và sửa giá trị thành viên
Giống struct
- Với biến, dùng
.. - Với con trỏ, dùng
->.
Hàm thành viên
Hàm thành viên là các hàm nằm trong lớp.
Ví dụ hàm thành viên phổ biến
1 2 3 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Lớp có hàm in Object và hàm đổi weight.
Giống hàm thường, có thể khai báo trước rồi định nghĩa sau như dòng 14 và 17.
Gọi var.print() để gọi hàm thành viên.
Nạp chồng toán tử
Nạp chồng là gì
C++ cho phép nhiều hàm hoặc toán tử cùng tên nhưng khác tham số gọi là nạp chồng (overload).
Nếu loại tham số hoặc số lượng khác nhau thì xem là hàm khác nhau.
Lưu ý: nếu chỉ khác kiểu trả về thì không thể nạp chồng, compiler sẽ báo lỗi.
Khi gọi không gây mơ hồ (thường mơ hồ nếu có tham số mặc định), compiler chọn hàm dựa vào tham số.
Quá trình đó gọi là phân giải nạp chồng.
Nạp chồng toán tử giúp đơn giản code.
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Ví dụ định nghĩa lớp Vector và nạp chồng * + - lần lượt là tích vô hướng, cộng, trừ vector.
Mẫu nạp chồng:
1 2 3 | |
Với lớp tự định nghĩa, nếu nạp chồng một số toán tử (thường chỉ cần <), có thể dùng STL container hoặc thuật toán tương ứng như sort.
Xem thêm mục “Tài liệu tham khảo” số 4.
Toán tử có thể nạp chồng
1 2 3 4 5 6 | |
Gán giá trị khởi tạo khi tạo biến
Cần định nghĩa constructor mặc định (Default constructor).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Ví dụ này định nghĩa constructor mặc định để khởi tạo weight và value bằng 0.
Nếu không định nghĩa constructor, compiler tạo constructor mặc định ngầm định, và khởi tạo theo kiểu của thành viên (như biến kiểu cơ bản).
Trong trường hợp đó, thành viên chưa được khởi tạo; đọc giá trị chưa khởi tạo là undefined.
Nếu cần khởi tạo giá trị khác, có thể định nghĩa (nạp chồng) constructor khác.
Về định nghĩa (nạp chồng) constructor
Constructor mặc định thường không có tham số, khác với constructor khác có tham số. Nếu đã định nghĩa constructor có tham số, compiler không tự sinh constructor mặc định, nên gọi không tham số sẽ lỗi.
Dùng C++11 trở lên có thể dùng {} để khởi tạo.
Về {}
Dùng {} sẽ sử dụng std::initializer_list để khởi tạo.
Quy trình đại khái:
- Tìm constructor nhận
std::initializer_list, nếu có thì gọi (xong thì dừng). - Thử điền phần tử
{}vào tham số constructor khác theo thứ tự; nếu điền đủ (kể cả tham số mặc định) thì gọi. - Nếu không có thành viên
private, thử gán ngoài lớp theo thứ tự khai báo hoặc chỉ số.
Quy trình trên là rút gọn, xem "Tài liệu tham khảo 9".
1 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 | |
Về chuyển kiểu ngầm định
Có thể gặp code:
1 2 3 4 5 6 7 8 | |
Nhìn có vẻ vô lý vì int không thể chuyển thành node, nhưng compiler không báo lỗi.
Lý do: khi gán, 1 được dùng để gọi node::node(int), sau đó gọi copy constructor để gán.
Thường người viết muốn compiler báo lỗi; khi đó thêm explicit trước constructor.
1 2 3 4 5 6 | |
Khi đó node a=1 sẽ lỗi, còn node a=node(1) thì không vì gọi rõ ràng constructor.
Trong thi đấu, thường tránh bằng cách tuân thủ quy tắc code.
Hủy
Mọi biến sẽ bị hủy khi hết phạm vi.
Với con trỏ trỏ đến vùng nhớ cấp phát động, khi bị hủy sẽ không tự giải phóng bộ nhớ; cần giải phóng thủ công.
Nếu struct có thành viên là con trỏ, cũng gặp vấn đề này, nên dùng destructor để giải phóng.
Destructor sẽ được gọi khi biến bị hủy. Cú pháp giống constructor nhưng thêm ~ phía trước.
Destructor mặc định thường đủ cho thi đấu; chỉ cần tự viết khi thành viên có con trỏ.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Gán cho biến lớp
Mặc định, gán sẽ theo quy tắc gán từng thành viên. Có thể dùng TênLớp() hoặc TênLớp{} làm biến tạm để gán.
Cách đầu chỉ gọi copy constructor, cách sau còn gọi constructor mặc định trước.
Mặc định là shallow copy. Nếu có con trỏ, sau khi gán, hai biến sẽ trỏ cùng địa chỉ.
1 2 3 4 | |
Muốn xử lý con trỏ hay thao tác khác cần nạp chồng constructor tương ứng.
Xem thêm “Tài liệu tham khảo” số 6 về constructor.
Tài liệu tham khảo
- cppreference class
- cppreference access
- cppreference default_constructor
- cppreference operator
- cplusplus Data structures
- cplusplus Special members
- C++11 FAQ
- cppreference Friendship and inheritance
- cppreference value initialization
Last updated on this page:, Update history
Found an error? Want to help improve? Edit this page on GitHub!
Contributors to this page:Ir1d, cjsoft, Lans1ot, JasonkayZK
All content on this page is provided under the terms of the CC BY-SA 4.0 and SATA license, additional terms may apply