C++: Bài 24: Tham chiếu

Một biến tham chiếu là một bí danh, nghĩa là một tên khác của một biến đã tồn tại. Khi một tham chiếu được khởi tạo với một biến, tên biến hoặc tên tham chiếu có thể được sử dụng để tham chiếu đến biến.

Tham chiếu so với Con trỏ

Tham chiếu thường bị nhầm lẫn với con trỏ nhưng có ba điểm khác biệt chính giữa tham chiếu và con trỏ là như sau:

  • Bạn không thể có tham chiếu NULL. Bạn phải luôn có thể giả định rằng một tham chiếu được kết nối với một phần hợp lệ của bộ lưu trữ.

  • Khi một tham chiếu được khởi tạo cho một đối tượng, nó không thể được thay đổi để tham chiếu đến một đối tượng khác. Con trỏ có thể được trỏ đến một đối tượng khác bất cứ lúc nào.

  • Khi ta tạo tham chiếu thì nó phải được khởi tạo. Con trỏ có thể được khởi tạo bất kỳ lúc nào.

Tạo tham chiếu trong C++

Hãy coi một tên biến như một nhãn gắn với vị trí của biến đó trong bộ nhớ. Sau đó, ta có thể coi một tham chiếu như một nhãn thứ hai được gắn vào vị trí bộ nhớ đó. Do đó, bạn có thể truy cập nội dung của biến thông qua tên biến ban đầu hoặc tham chiếu. Ví dụ:

int i = 17;
double d = 20;

Ta có thể khai báo các biến tham chiếu cho i như sau.

int& r = i;
double& s = d;

& đặt sau int thể hiện việc khai báo tham chiếu, cụ thể “r là tham chiếu số nguyên được khởi tạo thành i” và “s là tham chiếu double được khởi tạo thành d”. Ví dụ sau sử dụng các tham chiếu trên kiểu int và double:

#include <

iostream

> using namespace std; main(){ // khai báo các biến

int

i;

double

d; // khai báo các biến tham chiếu

int

& r = i;

double

& s = d; i =

5

; cout <<

"Gia tri cua i: "

<< i << endl; cout <<

"Gia tri cua tham chieu i: "

<< r << endl; d =

11.7

; cout <<

"Gia tri cua d: "

<< d << endl; cout <<

"Gia tri cua tham chieu d: "

<< s << endl;

return

0

; }

Khi đoạn mã trên được biên dịch cùng nhau và được thực thi, nó sẽ tạo ra kết quả sau:

Gia tri cua i: 5
Gia tri cua tham chieu i: 5
Gia tri cua d: 11.7
Gia tri cua tham chieu d: 11.7

Tham chiếu thường được sử dụng cho danh sách đối số hàm và giá trị trả về của hàm. Sau đây là hai chủ đề quan trọng liên quan đến tham chiếu trong C++ mà một lập trình viên C++ cần phải nắm rõ:

Tham chiếu dưới dạng tham số

Dưới đây là một ví dụ về lời gọi theo tham chiếu sử dụng tham chiếu trong C++:

#include <

iostream

> using namespace std; // khai báo hàm

void

swap(

int

& x,

int

& y); main() { // khai báo các biến local:

int

a =

100

;

int

b =

200

; cout <<

"Truoc khi swap, a = "

<< a << endl; cout <<

"Truoc khi swap, b = "

<< b << endl; /* gọi một hàm để swap các giá trị.*/ swap(a, b); cout <<

"Sau khi swap, a = "

<< a << endl; cout <<

"Sau khi swap, b = "

<< b << endl;

return

0

; } // định nghĩa hàm để swap các giá trị.

void

swap(

int

& x,

int

& y) {

int

temp; temp = x; /* lấy giá trị tại địa chỉ x lưu vào temp */ x = y; /* lưu y vào x */ y = temp; /* lưu x vào y (vì temp chứa x) */ }

Kết quả thực thi của đoạn code trên:

Truoc khi swap, a = 100
Truoc khi swap, b = 200
Sau khi swap, a = 200
Sau khi swap, b = 100

Tham chiếu dưới dạng giá trị trả về

Một chương trình C++ có thể dễ đọc và dễ bảo trì hơn bằng cách sử dụng tham chiếu thay vì con trỏ. Một hàm trong C++ có thể trả về một tham chiếu theo cách tương tự như nó trả về một con trỏ.

Khi một hàm trả về một tham chiếu, nó sẽ trả về một con trỏ không tường minh đến giá trị trả về của nó. Bằng cách này, một hàm có thể được sử dụng ở bên trái của một câu lệnh gán. Hãy xét chương trình đơn giản sau:

#include<

iostream

> #include<

ctime

> using namespace std;

double

vals[] = {

10.1

,

12.6

,

33.1

,

24.1

,

50.0

};

double

& setValues(

int

i) {

return

vals[i]; // trả về một tham chiếu tới phần tử chỉ số i } // hàm main sẽ gọi hàm được định nghĩa ở trên. main() { cout <<

"Gia tri truoc khi thay doi:"

<< endl;

for

(

int

i =

0

; i <

5

; i++ ) { cout <<

"vals["

<< i <<

"] = "

<< vals[i] << endl; } setValues(

1

) =

20.23

; // thay đổi phần tử chỉ số 1 setValues(

3

) =

70.8

; // thay đổi phần tử chỉ số 3 cout <<

"Sau khi thay doi:"

<< endl;

for

(

int

i =

0

; i <

5

; i++ ) { cout <<

"vals["

<< i <<

"] = "

<< vals[i] << endl; }

return

0

; }

Khi đoạn mã trên được biên dịch cùng nhau và được thực thi, nó sẽ tạo ra kết quả sau:

Gia tri truoc khi thay doi:
vals[0] = 10.1
vals[1] = 12.6
vals[2] = 33.1
vals[3] = 24.1
vals[4] = 50
Sau khi thay doi:
vals[0] = 10.1
vals[1] = 20.23
vals[2] = 33.1
vals[3] = 70.8
vals[4] = 50

Khi trả về một tham chiếu, hãy lưu ý rằng đối tượng được tham chiếu không được nằm ngoài phạm vi. Vì vậy, việc trả về một tham chiếu đến biến local là không hợp lệ. Nhưng bạn luôn có thể trả về một tham chiếu trên một biến tĩnh.

int

& func() {

int

q; // return q; // lỗi thời gian biên dịch

static int

x;

return

x; // an toàn, x tồn tại ngoài phạm vi }