Danh sách khởi tạo
Thuộc phần Lập trình hướng đối tượng trong hành trình C++ của Coddy. Bài học 22 trên 104.
Bạn đã thấy cú pháp dấu hai chấm trong các bài học trước, chẳng hạn như Player(std::string n, int h) : name(n), health(h) {}. Cách này gọi là danh sách khởi tạo thành viên, và đây là cách nên dùng để khởi tạo các thành viên trong C++.
Danh sách khởi tạo (initializer list) xuất hiện sau danh sách tham số của hàm khởi tạo (constructor), bắt đầu bằng dấu hai chấm. Mỗi thành viên được khởi tạo trực tiếp bằng giá trị của nó, các thành viên cách nhau bằng dấu phẩy:
class Rectangle {
int width;
int height;
const int id;
int& reference;
public:
Rectangle(int w, int h, int i, int& r)
: width(w), height(h), id(i), reference(r) {
// Thân hàm khởi tạo (có thể để trống)
}
};Tại sao nên dùng danh sách khởi tạo thay vì phép gán trong phần thân hàm khởi tạo? Với phép gán, các thành viên được khởi tạo mặc định trước, rồi mới được gán giá trị mới. Với danh sách khởi tạo, các thành viên được khởi tạo trực tiếp bằng giá trị chính xác, giúp tăng hiệu quả và đôi khi là điều bắt buộc.
Một số thành viên MUST phải được khởi tạo bằng danh sách:
- Các thành viên
const, không thể gán giá trị sau khi khởi tạo
- Các thành viên tham chiếu phải được liên kết khi khởi tạo
- Các thành viên không có hàm khởi tạo mặc định
class Player {
const int maxHealth; // Phải dùng danh sách khởi tạo
std::string name;
public:
// Cách này chạy được
Player(int max, std::string n) : maxHealth(max), name(n) {}
// Cách này KHÔNG biên dịch được:
// Player(int max) { maxHealth = max; } // Lỗi!
};Các thành viên được khởi tạo theo thứ tự chúng được khai báo trong lớp, không phải theo thứ tự trong danh sách khởi tạo. Luôn viết danh sách khởi tạo theo thứ tự khai báo để tránh nhầm lẫn.
Thử thách
DễHãy xây dựng một hệ thống cấu hình cho công cụ trò chơi để minh họa khi nào và vì sao danh sách khởi tạo thành viên là cần thiết. Bạn sẽ tạo một lớp GameConfig có các thành viên bắt buộc phải được khởi tạo bằng cú pháp danh sách khởi tạo.
Bạn sẽ tạo hai tệp để sắp xếp code:
GameConfig.h: Định nghĩa một lớpGameConfiglưu trữ các cài đặt trò chơi. Lớp của bạn cần có:- Một
const std::string gameName: tên trò chơi, không thể thay đổi sau khi tạo - Một
const int maxPlayers: số người chơi tối đa, cố định khi khởi tạo - Một
int& difficultyRef: tham chiếu đến một cài đặt độ khó bên ngoài - Một
int screenWidthvàint screenHeight: các thành viên thông thường dùng cho cài đặt hiển thị - Một hàm khởi tạo nhận tất cả tham số cần thiết và khởi tạo mọi thành viên bằng danh sách khởi tạo. Danh sách khởi tạo phải tuân theo thứ tự khai báo các thành viên trong lớp
- Một phương thức
display()in cấu hình theo định dạng sau:Game: <gameName> Max Players: <maxPlayers> Difficulty: <difficultyRef value> Resolution: <screenWidth>x<screenHeight>
- Một
main.cpp: Đọc các giá trị cấu hình từ đầu vào và tạo một đối tượng GameConfig. Bạn cần:- Đọc tên trò chơi (chuỗi), số người chơi tối đa (int), độ khó (int), chiều rộng (int) và chiều cao (int) từ đầu vào: mỗi giá trị trên một dòng riêng
- Tạo một biến
intcục bộ cho độ khó để cấu hình tham chiếu đến - Tạo một đối tượng
GameConfigbằng hàm khởi tạo sử dụng danh sách khởi tạo - Gọi
display()để hiển thị cấu hình ban đầu - Thay đổi trực tiếp biến độ khó bằng cách nhân nó với 2
- In ra
"After difficulty change:" - Gọi lại
display()để cho thấy tham chiếu phản ánh giá trị đã cập nhật
Thử thách này làm rõ vì sao danh sách khởi tạo rất quan trọng: bạn không thể gán giá trị cho các thành viên const và thành viên tham chiếu trong phần thân hàm khởi tạo. Bạn phải khởi tạo chúng trong danh sách. Thành viên tham chiếu cũng cho thấy những thay đổi đối với biến ban đầu được phản ánh qua đối tượng cấu hình như thế nào.
Thêm tệp tiêu đề vào main.cpp bằng #include "GameConfig.h".
Tự làm thử
#include <iostream>
#include <string>
#include "GameConfig.h"
using namespace std;
int main() {
// Đọc các giá trị đầu vào
string gameName;
getline(cin, gameName);
int maxPlayers;
cin >> maxPlayers;
int difficulty;
cin >> difficulty;
int width;
cin >> width;
int height;
cin >> height;
// TODO: Tạo đối tượng GameConfig bằng hàm khởi tạo sử dụng danh sách khởi tạo
// Hàm khởi tạo cần nhận: gameName, maxPlayers, difficulty (theo tham chiếu), width, height
// TODO: Gọi display() để hiển thị cấu hình ban đầu
// TODO: Thay đổi trực tiếp biến difficulty (nhân giá trị của biến với 2)
// TODO: In ra "After difficulty change:"
// TODO: Gọi lại display() để hiển thị cấu hình đã cập nhật
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về OOP
Tệp bên ngoàiBiên dịch và tạo bản dựng C++Tệp header và tệp nguồnNamespace và phạm viGiới thiệu về OOP trong C++Lớp và đối tượngCon trỏ thisPhương thức (hàm thành viên)Thuộc tính (dữ liệu thành viên)Cơ bản về hàm khởi tạo và hàm hủyÔn tập: máy tính đơn giản4Thuộc tính của lớp
Thành viên đối tượng và thành viên tĩnhHàm truy xuất và hàm thiết lậpHàm thành viên hằngTừ khóa mutablePhương thức và biến tĩnhHàm và lớp bạnÔn tập: Trình quản lý tài khoản ngân hàng7Kế thừa
Kế thừa cơ bảnMức truy cập trong kế thừaThứ tự gọi hàm khởi tạo và hàm hủyGhi đè phương thứcHàm ảo và VTableĐa kế thừaKế thừa ảoÔn tập: hệ phân cấp nhân viên2Quản lý bộ nhớ
Bộ nhớ ngăn xếp và heapCon trỏ và tham chiếuBộ nhớ động (new/delete)Con trỏ thông minh trong C++RAII trong C++Ôn tập: Trình quản lý mảng động5Tính đóng gói
Phạm vi truy cập trong C++Tìm hiểu sâu về phạm vi truy cậpẨn thông tinStruct và lớpLớp lồng nhau và lớp bên trongÔn tập: hệ thống hồ sơ sinh viên8Đa hình
Đa hình lúc biên dịch và khi chạyNạp chồng hàmÔn lại hàm ảoHàm ảo thuần túyLớp trừu tượngThiết kế interface trong C++Ép kiểu động và RTTIÔn tập: Máy tính hình học3Hàm khởi tạo và hàm hủy
Hàm khởi tạo mặc địnhHàm khởi tạo có tham sốHàm khởi tạo sao chépHàm khởi tạo di chuyểnDanh sách khởi tạoHàm khởi tạo ủy quyềnTìm hiểu sâu về hàm hủyQuy tắc 3 / 5 / 0Ôn tập: lớp chuỗi6Nạp chồng toán tử
Giới thiệu về nạp chồng toán tửNạp chồng toán tử số họcNạp chồng toán tử so sánhToán tử luồngNạp chồng toán tử gánNạp chồng toán tử [] và ()Toán tử chuyển đổi kiểuÔn tập: lớp ma trận9Mẫu
Mẫu hàmMẫu lớpChuyên biệt hóa mẫuMẫu biến tham sốCơ bản về SFINAE và đặc điểm kiểuÔn tập: vùng chứa tổng quátTự luyện tập: Trình biên dịch C++ online