Menu
CoddyTech

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.

challenge icon

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ớp GameConfig lư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 screenWidth và 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>
  • 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 int cục bộ cho độ khó để cấu hình tham chiếu đến
    • Tạo một đối tượng GameConfig bằ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;
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch C++ online