Cách sử dụng “const” trong C++

Tiếp theo bài viết kỳ trước, bài này xin trình bày tác dụng của const khi hoạt động với hàm trong C++.

Giả sử ta có cấu trúc sau:

class A
{
private:

int m_a;

public:

     …
};

Và ta có một hàm nhận tham số đầu vào là một đối tượng kiểu A và trả về một đối tượng kiểu A.

A functionName(A object);  // object là một đối tượng kiểu A

Khi gọi hàm này với x là một đối tượng kiểu A:

functionName(x);

Một đối tượng object sẽ được tạo và máy sẽ copy toàn bộ giá trị của x vào object rồi xử lý, sau đó sẽ trả về một đối tượng kiểu A. Vấn đề phát sinh là khi ta có một class với kích thước lớn thì việc chép giá trị vào tham số hình thức ở hàm sẽ làm cho chương trình trở nên chậm đi và đặc biệt là sẽ hao phí bộ nhớ. Do đó, ta có giải pháp là hai khai báo cho hàm đó như sau:

A functionName(A& object);
A functionName(A* object);

Hàm trên sử dụng tham số hình thức là một biến tham chiếu (reference variable) và hàm dưới dùng một biến con trỏ (pointer variable). Hai cách khai báo này sẽ cho kết quả tương tự nhau. (Sự khác nhau giữa biến tham chiếu và biến con trỏ sẽ được trình bày trong một bài gần đây). Khi này, với lời gọi hàm như trên thì địa chỉ của x sẽ được truyền vào, do đó sẽ tránh được việc phải chép cả cấu trúc với kích thước lớn.

Nhưng với khai báo như thế thì giá trị của biến truyền vào có thể bị thay đổi thông qua biến object (vì là biến con trỏ hoặc tham chiếu), trong khi với cách khai báo như cũ thì ta không hề muốn giá trị này bị sửa đổi chút nào. Do đó, từ khóa const được sử dụng:

A functionName(const A& object);
A functionName(const A* object);

Như bài trước, nếu ta muốn con trỏ object luôn trỏ đến một vị trí cố định thì hãy viết:

A functionName(const A* const object);

Nếu hàm trả về một con trỏ trỏ đến một đối tượng kiểu A:

A* functionName(const A* const object);

Theo nhu cầu, có thể bạn mong muốn hàm sẽ trả về con trỏ mà giá trị trỏ đến là không thay đổi được thông qua con trỏ, bạn sẽ khai báo hàm như sau:

const A* functionName(const A* const object);

Nếu bạn muốn con trỏ trả về cũng không thể thay đổi được vị trí trỏ đến của nó thì hãy viết:

const A* const functionName(const A* const object);

Tiếp theo, nếu class A của chúng ta có thêm một phương thức là long A::m_square() ; chẳng hạn. Phương thức này sẽ tính bình phương của biến A::m_a và trả về giá trị đó. Thực sự, đây chỉ là một hàm tính toán một giá trị từ giá trị của những thuộc tính trong class. Yêu cầu đặt ra là hàm này không được làm thay đổi giá trị của các thuộc tính của class. Do đó ta sẽ sử dụng một phương thức hằng bằng cách đặt từ const vào cuối hàm:

long A::m_square() const;

Bây giờ, nếu bạn lại muốn phương thức hằng của mình vẫn có thể thay đổi được giá trị của một số thuộc tính của class thì sao? Điều này vẫn có thể đạt được bằng cách sử dụng từ khóa mutable vào trước khai báo của thuộc tính nào bạn cần thay đổi giá trị bởi phương thức hằng. Chú ý là mutable không được dùng cho các biến staticconst.

Trở lại với hàm functionName(…) của ta. Khi hàm này là một phương thức của class A và bạn muốn nó trở thành một phương thức hằng thì ta có một khai báo khủng hoảng về từ khóa const:

const A* const functionName(const A* const object) const; // 5 từ const

Việc sử dụng những từ khoá const và vị trí của chúng trong chương trình là tùy theo mục đích và yêu cầu của ngữ cảnh hiện tại. Do đó, bạn hãy lưu ý khi làm việc với chúng.

– Phong Tran –