Từ khóa static trong C++ có nhiều ứng dụng quan trọng, ảnh hưởng đến phạm vi và vòng đời của biến cũng như cách hoạt động của các đối tượng.
1. Biến tĩnh cục bộ
Khi áp dụng static cho một biến cục bộ trong một hàm, nó có hai tác động chính:
- Giới hạn phạm vi: Biến hoặc hàm được khai báo là
staticở phạm vi toàn cục chỉ có thể truy cập được trong cùng một đơn vị biên dịch (file mã nguồn). Các đơn vị biên dịch khác không thể tham chiếu đến chúng. - Kéo dài vòng đời: Thông thường, biến cục bộ bị hủy khi ra khỏi phạm vi của khối lệnh. Tuy nhiên, nếu được khai báo là
static, biến cục bộ sẽ được lưu trữ trong vùng nhớ tĩnh và vòng đời của nó được kéo dài đến khi chương trình kết thúc. Nếu biến tĩnh cục bộ được khởi tạo, nó chỉ thực hiện việc khởi tạo một lần duy nhất khi hàm được gọi lần đầu. Các lần gọi tiếp theo sẽ sử dụng giá trị đã có mà không khởi tạo lại.
Xem xét ví dụ sau:
#include <iostream>
void demSoLanGoi() {
static int dem = 0; // Khởi tạo một lần duy nhất
dem++;
std::cout << dem << std::endl;
}
int main() {
demSoLanGoi(); // Lần gọi 1
demSoLanGoi(); // Lần gọi 2
demSoLanGoi(); // Lần gọi 3
return 0;
}
Kết quả của đoạn mã trên là:
1
2
3
Biến dem chỉ được khởi tạo là 0 duy nhất một lần khi demSoLanGoi() được gọi lần đầu. Các lần gọi sau chỉ đơn thuần tăng giá trị của nó.
2. Đối tượng lớp tĩnh cục bộ
Các đối tượng lớp được khai báo là static trong một hàm cũng tuân theo các quy tắc chung của biến tĩnh. Tuy nhiên, việc khởi tạo đối tượng tĩnh thường được thực hiện bởi hàm tạo (constructor). Nếu không được khởi tạo tường minh, các kiểu dữ liệu cơ bản tích hợp sẵn sẽ được khởi tạo ngầm định bởi trình biên dịch. Đối với các đối tượng lớp, hàm tạo (mặc định hoặc tường minh) là bắt buộc. Hàm tạo chỉ được gọi lần đầu tiên khi chương trình thực thi đến khai báo đối tượng đó.
Ví dụ minh họa:
#include <iostream>
class DuLieu {
int giaTri;
public:
DuLieu(int val = 0) : giaTri(val) {
std::cout << "Khoi tao DuLieu(" << giaTri << ")" << std::endl;
}
~DuLieu() {
std::cout << "Huy DuLieu(~" << giaTri << ")" << std::endl;
}
void ganGiaTri(int val) { giaTri = val; }
int layGiaTri() const { return giaTri; }
void tangLenMot() { giaTri++; }
};
void xuLyDuLieu() {
static DuLieu obj(10); // Khởi tạo static, chỉ gọi constructor lần đầu
std::cout << "Gia tri hien tai: " << obj.layGiaTri() << std::endl;
obj.tangLenMot();
}
int main() {
xuLyDuLieu(); // Lần gọi 1
xuLyDuLieu(); // Lần gọi 2
xuLyDuLieu(); // Lần gọi 3
return 0;
}
Kết quả chạy chương trình:
Khoi tao DuLieu(10)
Gia tri hien tai: 10
Gia tri hien tai: 11
Gia tri hien tai: 12
Huy DuLieu(~12)
Mặc dù hàm xuLyDuLieu() được gọi ba lần, đối tượng obj chỉ được tạo ra một lần duy nhất. Điều này được xác nhận bởi việc hàm tạo chỉ được gọi một lần và hàm hủy cũng chỉ được gọi một lần sau khi hàm main kết thúc.
3. Đối tượng lớp tĩnh toàn cục
Việc hủy các đối tượng tĩnh toàn cục diễn ra theo thứ tự ngược lại với thứ tự khởi tạo của chúng, và chỉ những đối tượng đã được tạo ra mới bị hủy. Quá trình hủy các đối tượng tĩnh toàn cục xảy ra khi hàm main kết thúc. Tuy nhiên, hàm tạo của các đối tượng toàn cục lại được gọi trước khi hàm main bắt đầu thực thi.