Lớp lưu trữ trong C++

Xin chào các bạn đang học lập trình! Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị qua thế giới của các lớp lưu trữ trong C++. Đừng lo lắng nếu bạn mới bắt đầu học lập trình; tôi sẽ là người hướng dẫn thân thiện của bạn, giải thích mọi thứ từng bước. Hãy cùng nhau khám phá nhé!

C++ Storage Classes

Lớp lưu trữ là gì?

Trước khi chúng ta đi vào chi tiết, hãy hiểu lớp lưu trữ là gì. Trong C++, các lớp lưu trữ xác định phạm vi (tính khả见) và thời gian tồn tại của các biến và hàm trong chương trình. Chúng cho biết cách biên dịch器 lưu trữ biến, liệu nó có thể truy cập từ các tệp khác hay không, và nó nên tồn tại trong bộ nhớ bao lâu.

Bây giờ, hãy cùng tìm hiểu chi tiết từng lớp lưu trữ.

Lớp lưu trữ auto

Từ khóa auto trong C++ đã thay đổi ý nghĩa của nó theo từng phiên bản. Trong C++ hiện đại (C++11 và sau), nó được sử dụng cho suy luận kiểu. Tuy nhiên, trong các phiên bản cũ, nó là một chỉ định lớp lưu trữ.

Sử dụng cũ (trước C++11):

int main() {
auto int x = 5;  // Tương đương với: int x = 5;
return 0;
}

Trong cách sử dụng cũ này, auto khẳng định một biến có thời gian tồn tại tự động. Tuy nhiên, đây là mặc định cho các biến cục bộ, vì vậy nó hiếm khi được sử dụng.

Sử dụng hiện đại (C++11 và sau):

int main() {
auto x = 5;  // x suy luận là int
auto y = 3.14;  // y suy luận là double
auto z = "Hello";  // z suy luận là const char*
return 0;
}

Trong C++ hiện đại, auto cho phép biên dịch器 suy luận kiểu của biến dựa trên phần tử khởi tạo. Nó đặc biệt hữu ích với các kiểu phức tạp hoặc khi kiểu có thể thay đổi trong tương lai.

Lớp lưu trữ register

Từ khóa register là một gợi ý cho biên dịch器 rằng biến này sẽ được sử dụng nhiều và nên được giữ trong một thanh ghi CPU để truy cập nhanh hơn.

#include <iostream>

int main() {
register int counter = 0;

for(int i = 0; i < 1000000; i++) {
counter++;
}

std::cout << "Counter: " << counter << std::endl;
return 0;
}

Trong ví dụ này, chúng ta đang đề xuất với biên dịch器 rằng counter nên được giữ trong một thanh ghi. Tuy nhiên, các biên dịch器 hiện đại thường rất thông minh và có thể tự động thực hiện các tối ưu hóa này, vì vậy register hiếm khi được sử dụng trong thực tế.

Lớp lưu trữ static

Từ khóa static có ý nghĩa khác nhau tùy thuộc vào nơi nó được sử dụng:

1. Biến cục bộ tĩnh

#include <iostream>

void countCalls() {
static int calls = 0;
calls++;
std::cout << "This function has been called " << calls << " times." << std::endl;
}

int main() {
for(int i = 0; i < 5; i++) {
countCalls();
}
return 0;
}

Trong ví dụ này, calls chỉ được khởi tạo một lần và giữ giá trị của nó giữa các cuộc gọi hàm. Kết quả đầu ra sẽ là:

This function has been called 1 times.
This function has been called 2 times.
This function has been called 3 times.
This function has been called 4 times.
This function has been called 5 times.

2. Thành viên tĩnh của lớp

class MyClass {
public:
static int objectCount;

MyClass() {
objectCount++;
}
};

int MyClass::objectCount = 0;

int main() {
MyClass obj1;
MyClass obj2;
MyClass obj3;

std::cout << "Number of objects created: " << MyClass::objectCount << std::endl;
return 0;
}

Ở đây, objectCount được chia sẻ giữa tất cả các thể hiện của MyClass. Kết quả đầu ra sẽ là:

Number of objects created: 3

Lớp lưu trữ extern

Từ khóa extern được sử dụng để khai báo một biến hoặc hàm toàn cục trong một tệp khác.

Tệp: globals.cpp

int globalVar = 10;

Tệp: main.cpp

#include <iostream>

extern int globalVar;  // Khai báo của globalVar

int main() {
std::cout << "Global variable value: " << globalVar << std::endl;
return 0;
}

Trong ví dụ này, globalVar được định nghĩa trong globals.cpp và được khai báo là extern trong main.cpp. Điều này cho phép main.cpp sử dụng biến được định nghĩa trong một tệp khác.

Lớp lưu trữ mutable

Từ khóa mutable cho phép một thành viên của một đối tượng const có thể được sửa đổi.

class Person {
public:
Person(int age) : age(age), cacheValid(false) {}

int getAge() const {
if (!cacheValid) {
cachedAge = heavyComputation();
cacheValid = true;
}
return cachedAge;
}

private:
int age;
mutable int cachedAge;
mutable bool cacheValid;

int heavyComputation() const {
// Simulate a heavy computation
return age;
}
};

int main() {
const Person p(30);
std::cout << p.getAge() << std::endl;  // Điều này được phép
return 0;
}

Trong ví dụ này, mặc dù p là const, chúng ta vẫn có thể sửa đổi cachedAgecacheValid vì chúng được đánh dấu là mutable.

Tóm tắt

Hãy tóm tắt các lớp lưu trữ chúng ta đã học trong bảng handy:

Lớp lưu trữ Mục đích
auto Suy luận kiểu (C++ hiện đại)
register Gợi ý truy cập nhanh hơn (ít sử dụng)
static Giữ giá trị giữa các cuộc gọi hàm hoặc chia sẻ giữa các thể hiện của lớp
extern Khai báo biến hoặc hàm trong tệp khác
mutable Cho phép sửa đổi trong đối tượng const

Nhớ rằng, việc hiểu các lớp lưu trữ là rất quan trọng để quản lý bộ nhớ hiệu quả và kiểm soát phạm vi của các biến. Khi bạn tiếp tục hành trình học C++, bạn sẽ thấy các khái niệm này trở nên quen thuộc. Chúc các bạn may mắn và vui vẻ lập trình!

Credits: Image by storyset