Nhắc lại khai báo biến
dataType variableName;
Với cú pháp như trên ta gọi là các cách khai báo tham trị (chủ yếu biến sẽ lưu giá trị), dù là con trỏ vẫn lưu giá trị (địa chỉ vùng nhớ cũng là 1 con số).
int age = 9;
int * pAge = &age;
int * matrix = new int[4 * 4];
Ta có age
, pAge
, matrix
là các biến được khai báo tham trị.
Vì sao C++ ra đời thêm tham chiếu
Với C không có khai báo dạng tham chiếu, như vậy để tương tác "qua trung gian" (thông qua địa chỉ) cần truyền địa chỉ. Giả sử hàm thay đổi giá trị 2 biến cần thông qua địa chỉ.
void Swap(int *addressParam1, int *addessParam2);
Khảo sát addressParam1
cho thấy thông tin từ biến này quá nhiều bao gồm:
- Biến này có thể là 1 cấp phát động.
- Biến này có thể cấp phát động.
- Có thể lấy được vùng nhớ của
addressParam1
tường minh trong khi có thể không có nhu cầu. - Bất tiện cho trường hợp nếu
addressParam1
là 1 đối tượng, vậy sẽ có các cú pháp dạng:addressParam1->age
addressParam1->get()
Từ các thông tin trên, cần thiết 1 phương pháp mới sao cho:
- Tiện lợi khi thao tác thông qua địa chỉ.
- Vẫn giữ được khả năng truyền địa chỉ.
- Trừu tượng hơn để không can thiệp trực tiếp vào địa chỉ.
- Ràng buộc không cho phép cấp phát động.
Cú pháp khai báo tham chiếu
Tham chiếu về cách hiểu đơn thuần có thể nghĩ đây là bí danh của một biến hoặc tên gọi khác của một biến đã có sẵn.
Về mặt kỹ thuật, tham chiếu là 1 cách tương tác vào vùng nhớ của biến khác nhưng lại không thể tương tác (lấy địa chỉ, gán địa chỉ tham chiếu mới) vào vùng nhớ trung gian này như trong trường hợp của con trỏ.
Cú pháp:
<Tên kiểu tham chiếu>& <Tên biến> = <Tên biến khác>
Ví dụ:
int age = 8;
int & rAge = age; // age == 8
int * matrix = new int[4 * 5];
int * & rMatrix[3] = 97; // matrix[3] = 97;
Phụ lục - hàm swap thay đổi giá trị 2 biến kinh điển
Khi đề cập đến tham chiếu trong C++, hàm swap
thường được nhắc đến như 1 ví dụ điển hình.
#include <iostream> using namespace std; void swap1(int param1, int param2) { int temp = param1; param1 = param2; param2 = temp; } void swap2(int& param1, int& param2) { int temp = param1; param1 = param2; param2 = temp; } int main() { int a = 1024; int b = 2048; cout << "a = " << a << ", b = " << b << endl; cout << "swap1: "; swap1(a, b); cout << "a = " << a << ", b = " << b << endl; cout << "swap2: "; swap2(a, b); cout << "a = " << a << ", b = " << b << endl; return 0; } // Difference between value parameter and reference parameter // a = 1024, b = 2048 // Swap1: a = 1024, b = 2048 // Swap2: a = 2048, b = 1024
swap1
: Truyền tham trị - param1
và param2
chỉ là 2 bản sao chép của a
và b

swap2
: Truyền tham chiếu - param1
và param2
cũng chính là a
và b

So sánh tham chiếu và con trỏ
Giữa con trỏ và tham chiếu có điểm tương đồng, tuy nhiên cũng có một số điểm khác biệt cơ bản về mặt ý nghĩa như sau:
- Một con trỏ có thể nhận giá trị null trong khi một tham chiếu thì không thể.
- Một con trỏ có thể khởi tạo bất cứ lúc nào, trong khi một tham chiếu phải được khởi tạo ngay khi nó được khai báo. Nói cách khác, câu lệnh sau trình biên dịch sẽ báo lỗi:
int &k; // error
- Một con trỏ có thể trỏ đến nhiều đối tượng, trong khi một tham chiếu chỉ được tham chiếu tới một đối tượng duy nhất.
- Con trỏ có thể lưu trữ vùng nhớ cấp phát động, tham chiếu thì không thể.