Const và function
(tiếp)
Hàm hằng
Giống như trường hợp của hằng con trỏ và con trỏ hằng, chúng ta cũng có hàm trả về giá trị hằng số và hàm hằng.
class City {
private:
int population;
public:
int getPopulation() const; // hàm hằng đây nek :p
int setArea();
}
Hai đặc tính chất quan trọng của hàm hằng
Hàm hằng là phương thức trong một class để đảm bảo 2 yếu tố:
- Không được phép thay đổi giá trị các trường của class trong hàm hằng
int City::getPopulation() const {
population = 0; //error
}
- Một hằng đối tượng của class chỉ được phép gọi các phương thức là hàm hằng
const City *Hanoi = new City();
Hanoi->setArea(); //error
Hanoi->getPopulation(); //OK
Thực ra, yếu tố thứ hai khá quan trọng bởi vì thông thường, để tối ưu hiệu năng trong C++, chúng ta sử dụng tham số kiểu hằng tham chiếu cho hàm như sau:
void setCity( const City &city);
Trong trường hợp đó, nếu class City không có những phương thức là hàm hằng, chúng ta sẽ gặp khó khăn khi muốn truy cập các giá trị của city trong thân hàm.
Overloading
Như các bạn đã biết, overloading là khái niệm cơ bản trong lập trình hướng đối tượng, là trường hợp một lớp có thể có nhiều phương thức trùng tên nhưng khác chữ ký hàm. Nhưng trong lập trình c++, ít ai để ý rằng, overloading có thể xảy ra với 2 phương thức trùng tên trong đó: một phương thức là hàm hằng và phương thức còn lại là hàm thông thường.
Sau đây là một trường hợp overloading dựa trên tính chất hằng của hàm:
class TextBox {
private:
char content[300];
public:
const char& operator[](std::size_t position) const { //OP 1
return content[position];
}
char& operator[](std::size_t position) { //OP 2
return content[position];
}
}
Như bạn thấy, chúng ta có thể khai báo 2 operator có chức năng hoàn toàn giống nhau là tham chiếu đến nội dung tại một vị trí cụ thể trong xâu ký tự của TexBox. Tuy nhiên, chỉ có một điểm khác: 1 operator là hàm hằng, còn một operator là hàm thông thường.
Lưu ý: bạn có thể thấy ở OP 1, chúng ta có đồng thời cả 2 khai báo const ( vị trí đầu và cuối hàm). Đó là do trình compiler muốn đảm bảo ràng buộc sau đây: nếu hàm trả về giá trị là tham chiếu và hàm đó là hàm hằng, thì gía trị tham chiếu trả về cũng phải là hằng.
Bạn có hơi băn khoăn, vậy trường hợp overloading kiểu này có ý nghĩa gì? Tất nhiên rồi, hãy cùng phân tích một vài trường hợp sau đây:
Trong trường hợp đối tượng TextBox là hằng:
void print(const TextBox &textBox);
Trong trường hợp trên, bạn không thể thực hiện điều này:
void print(const TextBox &textBox){
textBox[0] = 'a'; //error
}
Bởi vì toán tử [] được gọi trong trường hợp này là OP 1, là hàm trả về giá trị là hằng số. Đó cũng là lý dó compiler có sự ràng buộc trong lưu ý ở trên, bởi vì nếu ta có thể gán textBox[0] = ‘a’ trong ví dụ trên, nó vi phạm quy tắc: đối tượng hằng không thể thay đổi giá trị (trong trường hợp này là đối tượng textBox).
Còn trong trường hợp textBox không phải là đối tượng hằng thì sao?
TextBox textBox;
textBox [0] = 'a'; //OK
Ở trường hợp trên, toán tử []được gọi làOP 2, và việc thực hiện gán giá trị cho textBox[0] là hoàn toàn hợp lý.
Vậy, overloading dựa trên tính chất hằng của hàm đảm bảo cho ta 2 khả năng:
- Nếu đối tượng là hằng, hàm overload nó có thể sử dụng là hàm hằng.
- Nếu đối tượng không phải hằng, hàm overload nó có thể sử dụng là hàm thông thường.