Menu
CoddyTech

Thành viên đối tượng và thành viên tĩnh

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 27 trên 104.

Đến giờ, mọi biến thành viên bạn tạo đều thuộc về một đối tượng cụ thể. Khi tạo hai đối tượng Player, mỗi đối tượng có name và health riêng. Chúng được gọi là thành viên của instance: mỗi instance của lớp có các thành viên này riêng biệt.

Nhưng nếu bạn cần chia sẻ dữ liệu giữa tất cả các đối tượng thì sao? Ví dụ, theo dõi tổng số người chơi.

Đây là lúc thành viên tĩnh phát huy tác dụng. Thành viên tĩnh thuộc về chính lớp, không thuộc về một đối tượng cụ thể nào.

class Player {
    std::string name;           // Thành viên instance, mỗi người chơi có một thành viên riêng
    int health;                 // Thành viên instance
    
    static int playerCount;     // Thành viên tĩnh, dùng chung cho TẤT CẢ người chơi
    
public:
    Player(std::string n) : name(n), health(100) {
        playerCount++;          // Tăng lên khi có người chơi được tạo
    }
    
    ~Player() {
        playerCount--;          // Giảm xuống khi có người chơi bị hủy
    }
};

Điểm khác biệt chính: bạn truy cập các thành viên của instance thông qua một đối tượng, còn có thể truy cập các thành viên static thông qua chính tên lớp. Hãy xem các thành viên của instance là “mỗi đối tượng có một bản” và các thành viên static là “mỗi lớp có một bản”.

Thành viên của instanceThành viên tĩnh
Riêng biệt với từng đối tượngDùng chung cho mọi đối tượng
Được tạo khi đối tượng được tạoTồn tại trước khi có bất kỳ đối tượng nào được tạo
Truy cập qua đối tượng: player.healthTruy cập qua lớp: Player::playerCount

Các thành viên tĩnh hữu ích để làm bộ đếm, lưu giá trị cấu hình hoặc lưu dữ liệu thuộc về lớp thay vì từng đối tượng.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống kiểm kê sản phẩm theo dõi thông tin của từng sản phẩm và thống kê toàn bộ hàng tồn kho bằng các thành viên instance và thành viên static.

Bạn sẽ tạo hai tệp để sắp xếp code:

  • Product.h: Định nghĩa lớp Product để quản lý từng sản phẩm, đồng thời theo dõi dữ liệu toàn bộ hàng tồn kho. Lớp của bạn cần có:
    • Các thành viên instance (instance): name (chuỗi) và price (số thực), riêng biệt cho từng sản phẩm
    • Một thành viên static totalProducts để đếm số sản phẩm hiện có
    • Một thành viên static totalValue để theo dõi tổng giá của tất cả sản phẩm
    • Một hàm khởi tạo nhận tên và giá, khởi tạo các thành viên instance, đồng thời cập nhật cả hai bộ đếm static (tăng số lượng, cộng giá vào tổng giá trị)
    • Một hàm hủy cập nhật các bộ đếm static khi một sản phẩm bị xóa (giảm số lượng, trừ giá khỏi tổng giá trị)
    • Một phương thức getInfo() trả về một chuỗi theo định dạng: "<name>: $<price>"
    • Một phương thức static getInventoryStats() trả về chuỗi: "Products: <count>, Total Value: $<value>"
  • main.cpp: Minh họa cách các thành viên static được dùng chung cho mọi đối tượng, trong khi các thành viên instance vẫn riêng biệt. Đọc tên và giá của hai sản phẩm từ đầu vào (name1, price1, name2, price2, mỗi giá trị trên một dòng riêng), sau đó:
    • In thống kê hàng tồn kho ban đầu bằng tên lớp: Product::getInventoryStats()
    • Tạo một khối phạm vi bằng dấu ngoặc nhọn, rồi tạo bên trong khối một Product với tên và giá đầu tiên. In thông tin của sản phẩm và thống kê hàng tồn kho hiện tại.
    • Sau khi kết thúc khối (sản phẩm đầu tiên bị hủy), in "After first scope:", sau đó in thống kê hàng tồn kho
    • Tạo một Product khác với tên và giá thứ hai. In thông tin của sản phẩm và thống kê hàng tồn kho.
    • Tạo sản phẩm Product thứ ba với tên "Bonus" và giá 5.00. In thống kê hàng tồn kho cuối cùng.

Nhớ định nghĩa các thành viên static bên ngoài lớp trong tệp tiêu đề (sau phần định nghĩa lớp). Định dạng giá với hai chữ số thập phân bằng std::fixed và std::setprecision(2) từ <iomanip>.

Thêm tệp tiêu đề vào main.cpp bằng #include "Product.h".

Tự làm thử

#include <iostream>
#include <string>
#include "Product.h"

using namespace std;

int main() {
    // Đọc đầu vào
    string name1;
    double price1;
    string name2;
    double price2;
    
    getline(cin, name1);
    cin >> price1;
    cin.ignore();
    getline(cin, name2);
    cin >> price2;
    
    // TODO: In ra thống kê kho hàng ban đầu bằng Product::getInventoryStats()
    
    // TODO: Tạo một khối lệnh với dấu ngoặc nhọn
    // Bên trong khối lệnh:
    //   - Tạo một Product với name1 và price1
    //   - In ra thông tin của đối tượng bằng getInfo()
    //   - In ra thống kê kho hàng hiện tại
    
    // TODO: Sau khi khối lệnh kết thúc, in ra "After first scope:"
    // Sau đó in ra thống kê kho hàng
    
    // TODO: Tạo một Product khác với name2 và price2
    // In ra thông tin của đối tượng và thống kê kho hàng
    
    // TODO: Tạo Product thứ ba có tên "Bonus" và giá 5.00
    // In ra thống kê kho hàng cuối cùng
    
    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