
Chào các "coder nhí" tương lai, hôm nay chúng ta sẽ "giải mã" một từ khóa mà nhìn qua thì tưởng "vô thưởng vô phạt" nhưng thực chất lại là "vệ sĩ" đắc lực cho code của các bạn: const. Thầy Creyt gọi nó là cái "khóa vĩnh cửu" hay "lời thề bất di bất dịch" trong thế giới lập trình C++. Hiểu nôm na, khi bạn "const-hóa" một thứ gì đó, bạn đang cam kết rằng thứ đó sẽ không bao giờ thay đổi sau khi được khởi tạo. Giống như bạn đăng một cái story "chỉ xem" trên Instagram vậy, không ai có thể chỉnh sửa nó được nữa.
1. const là gì và để làm gì?
const trong C++ là một từ khóa dùng để chỉ định rằng một giá trị, một biến, một con trỏ, hay thậm chí là một hàm thành viên sẽ không bị thay đổi. Nó như một "hợp đồng" với compiler và cả những đồng đội lập trình của bạn: "Ê, cái này là bất biến đó, đừng có mà động vào!".
Để làm gì ư? Đơn giản thôi:
- Ngăn chặn lỗi ngớ ngẩn (Bug Prevention): Tránh việc vô tình thay đổi một giá trị quan trọng mà đáng lẽ ra phải giữ nguyên. Tưởng tượng bạn có một hằng số
PI = 3.14159mà lỡ tay gánPI = 3ở đâu đó. Compiler sẽ la làng lên ngay nếu bạn dùngconst. - Tăng tính minh bạch (Clarity): Khi nhìn vào code, ai cũng biết ngay biến này, tham số này là read-only. Code rõ ràng hơn, dễ đọc hơn, dễ bảo trì hơn.
- Tối ưu hiệu suất (Performance Optimization): Compiler có thể thực hiện một số tối ưu hóa nhất định với các giá trị
constvì nó biết chúng sẽ không thay đổi. - An toàn dữ liệu (Data Safety): Đặc biệt quan trọng khi làm việc với các hệ thống lớn, nơi dữ liệu nhạy cảm cần được bảo vệ tuyệt đối.
Nói cách khác, const giúp code của bạn "trưởng thành" hơn, "đáng tin cậy" hơn, giống như một người bạn luôn giữ lời hứa vậy.
2. Code Ví Dụ Minh Họa Rõ Ràng
Chúng ta sẽ xem const hoạt động như thế nào với các "thể loại" khác nhau trong C++.
2.1. const với Biến Thường
Đây là trường hợp cơ bản nhất. Biến const phải được khởi tạo ngay lập tức và không thể thay đổi giá trị sau đó.
#include <iostream>
#include <string>
int main() {
// Khai báo một hằng số số nguyên
const int MAX_USERS = 100;
std::cout << "Max Users: " << MAX_USERS << std::endl;
// MAX_USERS = 120; // Lỗi: không thể gán giá trị cho biến const
// Khai báo một hằng số chuỗi
const std::string APP_VERSION = "1.0. BETA";
std::cout << "App Version: " << APP_VERSION << std::endl;
// APP_VERSION = "2.0"; // Lỗi tương tự
return 0;
}
2.2. const với Con Trỏ (Pointers)
Phần này hơi "xoắn não" một chút, nhưng cực kỳ quan trọng. const có thể áp dụng cho bản thân con trỏ hoặc cho dữ liệu mà con trỏ trỏ tới.
-
Con trỏ tới dữ liệu
const(const T*hoặcT const*): Con trỏ có thể thay đổi để trỏ đến một vị trí khác, nhưng dữ liệu mà nó đang trỏ tới thì không thể thay đổi thông qua con trỏ này. (Tưởng tượng bạn có một bản đồ chỉ đường, bạn có thể đổi sang bản đồ khác, nhưng không được vẽ thêm nhà lên bản đồ hiện tại).int value = 10; const int* ptr_to_const_value = &value; // Con trỏ tới một int const // *ptr_to_const_value = 20; // Lỗi: không thể thay đổi giá trị thông qua con trỏ này std::cout << "Value (via ptr_to_const_value): " << *ptr_to_const_value << std::endl; int another_value = 30; ptr_to_const_value = &another_value; // OK: con trỏ có thể trỏ tới chỗ khác std::cout << "Value (via ptr_to_const_value after re-assignment): " << *ptr_to_const_value << std::endl; -
Con trỏ
consttới dữ liệu khôngconst(T* const): Con trỏ không thể thay đổi để trỏ đến một vị trí khác sau khi khởi tạo, nhưng dữ liệu mà nó trỏ tới thì có thể thay đổi thông qua con trỏ này. (Bản đồ này không thể đổi sang bản đồ khác, nhưng bạn có thể vẽ lên nó).
int data = 50; int* const const_ptr = &data; // Con trỏ const tới một int *const_ptr = 60; // OK: có thể thay đổi giá trị mà con trỏ trỏ tới std::cout << "Data (via const_ptr): " << *const_ptr << std::endl; int new_data = 70; // const_ptr = &new_data; // Lỗi: không thể gán lại con trỏ const -
Con trỏ
consttới dữ liệuconst(const T* const): Cả con trỏ và dữ liệu mà nó trỏ tới đều không thể thay đổi. (Bản đồ này không thể đổi, cũng không được vẽ lên).int final_data = 80; const int* const final_const_ptr = &final_data; // Cả con trỏ và dữ liệu đều const // *final_const_ptr = 90; // Lỗi // final_const_ptr = &another_value; // Lỗi std::cout << "Final Data (via final_const_ptr): " << *final_const_ptr << std::endl;
2.3. const với Tham Số Hàm (Function Parameters)
Sử dụng const với tham số hàm là một "best practice" cực kỳ quan trọng, đặc biệt khi truyền tham chiếu hoặc con trỏ, để đảm bảo hàm không làm thay đổi dữ liệu gốc.
void print_vector_elements(const std::vector<int>& vec) {
// Tham số `vec` là const reference, đảm bảo hàm không sửa đổi vector gốc.
for (int x : vec) {
std::cout << x << " ";
}
std::cout << std::endl;
// vec[0] = 99; // Lỗi: không thể thay đổi phần tử của vector const
}
void process_string(const std::string* s) {
// Tham số `s` là con trỏ tới const string.
std::cout << "Processing string: " << *s << std::endl;
// *s = "new string"; // Lỗi: không thể thay đổi string gốc
}
int main() {
std::vector<int> my_vec = {1, 2, 3, 4, 5};
print_vector_elements(my_vec);
std::string my_str = "Hello C++";
process_string(&my_str);
return 0;
}
2.4. const với Hàm Thành Viên (Member Functions)
Khi một hàm thành viên của một lớp được đánh dấu là const, nó cam kết không thay đổi trạng thái (dữ liệu thành viên) của đối tượng mà nó được gọi trên đó. Nó chỉ có thể gọi các hàm const khác của đối tượng đó.
class User {
private:
std::string username;
int id;
public:
User(std::string name, int userID) : username(name), id(userID) {}
// Hàm const: không thay đổi trạng thái của đối tượng User
std::string get_username() const {
// username = "new_name"; // Lỗi: không thể thay đổi thành viên trong hàm const
return username;
}
int get_id() const {
return id;
}
// Hàm không const: có thể thay đổi trạng thái của đối tượng User
void set_username(const std::string& new_name) {
username = new_name;
}
};
int main() {
const User admin("admin_creyt", 101); // Đối tượng admin là const
std::cout << "Admin Username: " << admin.get_username() << std::endl;
// admin.set_username("super_admin"); // Lỗi: không thể gọi hàm non-const trên đối tượng const
User guest("guest_user", 202); // Đối tượng guest không const
std::cout << "Guest Username (before): " << guest.get_username() << std::endl;
guest.set_username("guest_updated"); // OK
std::cout << "Guest Username (after): " << guest.get_username() << std::endl;
return 0;
}

3. Mẹo (Best Practices) để ghi nhớ và dùng thực tế
- "Const-correctness" là vàng: Hãy tập thói quen dùng
constở mọi nơi có thể. Nếu một biến, tham số, hay hàm không cần thay đổi dữ liệu, hãy đánh dấu nó làconst. Compiler sẽ là "thầy giáo khó tính" nhắc nhở bạn nếu bạn lỡ tay vi phạm. - Đọc
consttừ phải sang trái (với con trỏ):int * const p(con trỏplàconst),const int * p(giá trị màptrỏ tới làconst). Mẹo này giúp bạn "giải mã" mấy cái con trỏconstphức tạp. - Tham chiếu
const(const references) cho tham số hàm: Khi bạn muốn truyền một đối tượng lớn vào hàm mà không muốn copy nó (để tối ưu hiệu suất) và cũng không muốn hàm sửa đổi nó, hãy dùngconst T&. Ví dụ:void process_data(const BigObject& data);. constmember functions: Đánh dấu các hàm thành viên không làm thay đổi trạng thái của đối tượng làconst. Điều này cực kỳ quan trọng để đảm bảo tính toàn vẹn của đối tượng và cho phép bạn gọi chúng trên các đối tượngconst.- Trường hợp ngoại lệ:
mutable: Đôi khi, bạn có một thành viên dữ liệu cần thay đổi ngay cả trong một hàmconst(ví dụ: một bộ đếm số lần gọi hàm, hoặc cache). Khi đó, bạn có thể đánh dấu thành viên đó làmutable. Tuy nhiên, hãy dùng nó một cách thận trọng, vì nó "phá vỡ" lời thềconst.
4. Ứng dụng thực tế các website/ứng dụng đã sử dụng
const không phải là một tính năng "trên trời" mà nó được sử dụng rộng rãi trong mọi ngóc ngách của các hệ thống phần mềm lớn:
- Game Engines (Unity, Unreal Engine): Các hằng số vật lý (trọng lực, tốc độ ánh sáng), cấu hình trò chơi không thay đổi, các đối tượng game state chỉ đọc thường được khai báo
constđể đảm bảo tính ổn định và hiệu suất. - Operating Systems (Linux Kernel): Trong nhân Linux, rất nhiều cấu trúc dữ liệu, tham số hệ thống, và chuỗi ký tự được đánh dấu
constđể bảo vệ chúng khỏi các thao tác ghi không mong muốn, đảm bảo tính bảo mật và ổn định của hệ thống. - Standard Template Library (STL) của C++: Các iterator (ví dụ
std::vector::const_iterator), các hàm thành viên nhưsize(),empty()của các container đều làconstmember functions. Các thuật toán nhưstd::for_eachthường nhận tham chiếuconst. - Thư viện đồ họa (OpenGL, DirectX): Các ma trận biến đổi (transformation matrices), màu sắc, tọa độ texture thường được truyền dưới dạng
constreference hoặcconstpointer để tránh sửa đổi và tối ưu hóa. - Web APIs và Backend Systems: Khi bạn có các đối tượng dữ liệu (DTO - Data Transfer Objects) chỉ dùng để gửi hoặc nhận thông tin mà không cần thay đổi, chúng thường được xử lý thông qua các tham chiếu
constđể đảm bảo tính toàn vẹn của dữ liệu.
5. Thử nghiệm đã từng và hướng dẫn nên dùng cho case nào
Thầy Creyt đã "chinh chiến" với const từ những ngày đầu và khẳng định nó là một trong những "người bạn" tốt nhất của lập trình viên C++.
Khi nào nên dùng const?
- Khai báo hằng số: Bất cứ khi nào bạn có một giá trị không bao giờ thay đổi trong suốt vòng đời của chương trình (ví dụ:
const double PI = 3.14159;). - Tham số hàm: Khi bạn truyền dữ liệu vào một hàm và bạn muốn đảm bảo hàm đó không sửa đổi dữ liệu gốc. Đây là một "rule of thumb" quan trọng để tránh side effects không mong muốn.
- Con trỏ và tham chiếu: Khi bạn muốn một con trỏ chỉ được đọc dữ liệu, hoặc một tham chiếu chỉ được xem dữ liệu, không được thay đổi (ví dụ:
const std::string& name). - Hàm thành viên của lớp: Khi một hàm không làm thay đổi trạng thái của đối tượng (không sửa đổi bất kỳ thành viên dữ liệu nào của đối tượng), hãy đánh dấu nó là
const. Điều này cho phép bạn gọi hàm đó trên các đối tượngconstvà giúp người khác hiểu rõ mục đích của hàm.
Khi nào KHÔNG nên dùng const?
- Khi bạn muốn và cần thay đổi giá trị của một biến. Đơn giản vậy thôi.
Sử dụng const một cách thông minh sẽ nâng tầm code của bạn lên một đẳng cấp mới: an toàn hơn, dễ hiểu hơn, và chuyên nghiệp hơn. Hãy coi nó như một "vũ khí bí mật" để "bảo vệ vương quốc dữ liệu" của bạn, các "Gen Z coder" nhé!
Thuộc Series: C++
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!