Menu
CoddyTech

Phương thức và biế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 31 trên 104.

Bạn đã học rằng các thành viên tĩnh được dùng chung cho tất cả đối tượng. Bây giờ, hãy cùng tìm hiểu cách định nghĩa và sử dụng phương thức tĩnh đúng cách cùng với các biến tĩnh.

Các biến tĩnh được khai báo bên trong lớp phải được định nghĩa bên ngoài lớp, thường là trong tệp mã nguồn. Vì các thành viên tĩnh cần một vị trí lưu trữ duy nhất được dùng chung cho tất cả đối tượng:

class Counter {
    static int count;    // Chỉ khai báo
    
public:
    Counter() { count++; }
    ~Counter() { count--; }
    
    static int getCount() {    // Phương thức tĩnh
        return count;
    }
};

// Định nghĩa: phải đặt bên ngoài lớp
int Counter::count = 0;

Phương thức tĩnh thuộc về lớp, không thuộc về bất kỳ đối tượng nào. Điều này dẫn đến một hệ quả quan trọng: phương thức tĩnh không thể truy cập các thành viên của instance hoặc dùng con trỏ this, vì chúng không được gọi trên một đối tượng cụ thể nào.

class MathUtils {
    static double pi;
    
public:
    static double circleArea(double radius) {
        return pi * radius * radius;    // Có thể truy cập các thành viên tĩnh
    }
};

double MathUtils::pi = 3.14159;

// Được gọi mà không cần đối tượng
double area = MathUtils::circleArea(5.0);

Phương thức tĩnh phù hợp với các hàm tiện ích không cần trạng thái của đối tượng, các hàm khởi tạo tạo đối tượng hoặc việc truy cập dữ liệu tĩnh. Vì chúng không cần một đối tượng cụ thể, bạn gọi chúng bằng tên lớp và toán tử phân giải phạm vi: ClassName::methodName().

challenge icon

Thử thách

Dễ

Hãy tạo một hệ thống sinh ID duy nhất để minh họa cách các phương thức và biến static kết hợp với nhau, cung cấp chức năng ở cấp lớp mà không cần tạo đối tượng.

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

  • IDGenerator.h: Định nghĩa lớp IDGenerator để tạo các ID duy nhất theo thứ tự. Lớp của bạn cần có:
    • Một thành viên static riêng tư nextId theo dõi ID tiếp theo cần gán (bắt đầu từ 1000)
    • Một thành viên static riêng tư prefix (chuỗi) lưu tiền tố có thể tùy chỉnh cho tất cả ID
    • Một phương thức static setPrefix(const std::string& p) để thay đổi tiền tố
    • Một phương thức static getNextId() trả về chuỗi kết hợp tiền tố với số ID hiện tại, sau đó tăng nextId để dùng cho lần gọi tiếp theo
    • Một phương thức static peekNextId() trả về ID tiếp theo sẽ là gì mà không tăng bộ đếm
    • Một phương thức static getGeneratedCount() trả về số ID đã được tạo (gợi ý: tính từ giá trị ban đầu và nextId hiện tại)
    • Một phương thức static reset() đặt lại nextId về 1000

    Nhớ định nghĩa các thành viên static bên ngoài lớp, sau phần định nghĩa lớp. Khởi tạo prefix thành "ID-".

  • main.cpp: Minh họa cách gọi các phương thức static bằng tên lớp mà không tạo đối tượng nào. Đọc một tiền tố tùy chỉnh từ đầu vào, sau đó:
    • In "Peek: <peeked_id>" bằng peekNextId()
    • Tạo ba ID bằng getNextId() và in từng ID dưới dạng "Generated: <id>"
    • In "Count: <count>" để hiển thị số ID đã được tạo
    • Thay đổi tiền tố bằng giá trị đầu vào với setPrefix()
    • Tạo thêm hai ID và in từng ID dưới dạng "Generated: <id>"
    • In lại "Count: <count>"
    • Gọi reset()
    • In "After reset - Peek: <peeked_id>"
    • In "After reset - Count: <count>"

Lưu ý rằng bạn không bao giờ tạo đối tượng IDGenerator: tất cả phương thức đều được gọi trực tiếp trên lớp bằng IDGenerator::methodName(). Đây là đặc điểm chính của phương thức static: chúng thuộc về chính lớp, không thuộc về bất kỳ đối tượng cụ thể nào.

Thêm <string> và <iostream> vào tệp tiêu đề, và đừng quên header guard.

Tự làm thử

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

using namespace std;

int main() {
    // Đọc tiền tố tùy chỉnh từ đầu vào
    string customPrefix;
    cin >> customPrefix;
    
    // TODO: Dùng peekNextId() để in ra "Peek: <peeked_id>"
    
    // TODO: Dùng getNextId() tạo ba ID và in từng ID dưới dạng "Generated: <id>"
    
    // TODO: In ra "Count: <count>" để cho biết đã tạo bao nhiêu ID
    
    // TODO: Dùng setPrefix() để đổi tiền tố theo giá trị đầu vào
    
    // TODO: Tạo thêm hai ID và in từng ID dưới dạng "Generated: <id>"
    
    // TODO: In lại "Count: <count>"
    
    // TODO: Gọi reset()
    
    // TODO: In ra "After reset - Peek: <peeked_id>"
    
    // TODO: In ra "After reset - Count: <count>"
    
    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