Bài 37. Biến và hàm thành viên hằng

Hiện mục lục Hiện mục lục

1. Biến thành viên hằng

Các biến thành viên có kiểu không phải tham chiếu có thể có kiểu hằng (const). Giống như các biến hằng đã được trình bày, sau khi được khởi tạo thì các biến thành viên hằng không thể được thay đổi.

Ở đây thì ta có thể thấy một lí do tại sao ta phải dùng danh sách khởi tạo thành viên trong hàm tạo. Nó có thể cho các biến thành viên giá trị khởi tạo; sau đó, khi thân hàm tạo được chạy, các biến thành viên hằng sẽ không thể được thay đổi nữa, vì thế ta không thể dùng phép gán với chúng được.

Lấy ví dụ lớp DaGiac, nếu ta để các biến thành viên là const thì ta sẽ không thể thay đổi chúng được:

class DaGiac {
	public:
		const int soCanh = 4;
		const double doDaiCanh = 5.;

		DaGiac(const int soCanh, const double doDaiCanh): soCanh{soCanh}, doDaiCanh{doDaiCanh} {}
};

int main() {
	DaGiac batGiac{8, 6.5};
	
	// ! KHÔNG HỢP LỆ.
	batGiac.doDaiCanh = 15.3;
}

Nếu biến thành viên có kiểu là tham chiếu thì const sẽ áp dụng vào tham chiếu như bình thường, nhớ lại rằng ta không thể thay đổi đích tham chiếu của một biến tham chiếu được. Và như vậy một biến tham chiếu cũng sẽ phải được khởi tạo qua con đường danh sách khởi tạo thành viên hoặc phần khởi tạo mặc định.

Ghi chú

Trong ngôn ngữ Java không có danh sách khởi tạo thành viên, thì ngôn ngữ có cơ chế là các trường final ("trường" tương tự với biến thành viên trong C++, "final" nghĩa là "cuối cùng" tương tự với biến hằng trong C++) sẽ có một cơ hội được gán trong thân hàm tạo để lấy làm việc khởi tạo. Nếu thân hàm tạo không thực hiện gán thì đó là lỗi ngữ nghĩa.

2. Hàm thành viên hằng

a) Cơ chế cơ bản

Theo mặc định, C++ coi như việc gọi các hàm thành viên là có khả năng làm thay đổi dữ liệu của thực thể, vì thế ta sẽ không thể gọi chúng trên các biến thực thể const. Nhưng có những hàm thành viên thực sự không thực hiện thay đổi dữ liệu, vì vậy C++ có cơ chế khai báo các hàm đó sử dụng từ khóa const.

Cụ thể, ta thực hiện đặt từ khóa const vào sau dấu đóng ngoặc ) của danh sách đối đầu vào. Khi đó thì hàm thành viên sẽ không thể thực hiện thao tác có khả năng thay đổi dữ liệu của các biến thành viên, và ta có thể thực hiện gọi hàm thành viên đó từ một thực thể const.

Lấy ví dụ lớp DaGiac có hàm thành viên chuVi như sau:

class DaGiac {
	public:
		const int soCanh = 4;
		const double doDaiCanh = 5.;

		DaGiac(const int soCanh, const double doDaiCanh): soCanh{soCanh}, doDaiCanh{doDaiCanh} {}

		double chuVi() {
			return soCanh * doDaiCanh;
		}
};

Sau đó trong một hàm ta tạo một thực thể const:

const DaGiac lucGiac(6, 4.8);

Nếu ta thực hiện gọi lucGiac.chuVi(), bộ dịch vẫn sẽ báo lỗi do kể cả khi hàm chuVi không thực hiện thay đổi gì lên các biến thành viên, ngôn ngữ vẫn coi như nó có khả năng thay đổi. Để có thể gọi được ta cần làm cho hàm chuVi là một hàm thành viên hằng:

double chuVi() const {
	return soCanh * doDaiCanh;
}

b) Nạp chồng hàm thành viên hằng và không hằng

Việc có const hay không cũng là một trong những yếu tố được xét để phân biệt các hàm thành viên. Vì vậy, ta có thể khai báo hai hàm thành viên cùng tên, cùng danh sách kiểu đối đầu vào nhưng một hàm có const và một hàm thì không. Khi thực hiện gọi, nếu thực thể là const thì hàm thành viên const sẽ được gọi, nếu không thì hàm thành viên không có const sẽ được gọi.

Mục lục Đóng mục lục