close up photo of programming of codes

codecungnhau.com

Một trang web về kỹ thuật lập trình

Tham chiếu trong C++

Khi một biến được khai báo là một tham chiếu, nó sẽ trở thành một tên thay thế cho một biến hiện có. Một biến có thể được khai báo như một tham chiếu bằng cách đặt ‘&’ vào khai báo.

#include<iostream>
using namespace std;
 
int main()
{
  int x = 10;
 
  // ref là một tham chiếu đến x.
  int& ref = x;
 
  // Giá trị của x giờ là 20
  ref = 20;
  cout << "x = " << x << endl ;
 
  // Giá trị của x giờ đổi thành 30
  x = 30;
  cout << "ref = " << ref << endl ;
 
  return 0;
}

Kết quả:
x = 20
ref = 30

Ứng dụng

1. Sửa đổi các tham số đã truyền trong một hàm

Nếu một hàm nhận được một tham chiếu đến một biến, nó có thể sửa đổi giá trị của biến đó. Ví dụ, các biến chương trình sau đây được hoán đổi bằng cách sử dụng các tham chiếu.

#include<iostream>
using namespace std;
 
void swap (int& first, int& second)
{
    int temp = first;
    first = second;
    second = temp;
}
 
int main()
{
    int a = 2, b = 3;
    swap( a, b );
    cout << a << " " << b;
    return 0;
}

Kết quả: 3 2

Chúng ta cũng có thể sử dụng các tham chiếu trong vòng lặp for each để sửa đổi tất cả các phần tử.

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    vector<int> vect{ 10, 20, 30, 40 };
 
    // Ta có thể chính sửa các
    // phần tử sử dụng tham chiếu.
    for (int &x : vect)
        x = x + 5;
 
    // Hiển thị các phần tử.
    for (int x : vect)
       cout << x << " ";
 
    return 0;
}

2. Tránh sao chép các cấu trúc lớn

Hãy tưởng tượng một hàm phải nhận một đối tượng lớn. Nếu chúng ta chuyển nó mà không có tham chiếu, một bản sao mới của nó sẽ được tạo ra, gây lãng phí thời gian và bộ nhớ của CPU. Chúng ta có thể sử dụng tham chiếu để tránh điều này.

struct Student {
   string name;
   string address;
   int rollNo;
}
 
// Nếu ta bỏ &, một bản sao của
// đối tượng student sẽ được tạo
// Ta sử dụng const để tránh các thay đổi
// không mong muốn trên đối tượng vì hàm 
// chỉ dùng để hiện thị đối tượng.
void print(const Student &s)
{
    cout << s.name << "  " << s.address << "  " << s.rollNo;
}

Tương tự, đối với vòng lặp for each.

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    vector<string> vect{"geeksforgeeks practice",
                     "geeksforgeeks write",
                     "geeksforgeeks ide"};
 
    // Tránh sao Chép toàn bộ đối
    // tượng chuỗi bằng tham chiếu.
    for (const auto &x : vect)
       cout << x << endl;
 
    return 0;
}

Tham chiếu và con trỏ

Cả hai tham chiếu và con trỏ đều có thể được sử dụng để thay đổi các biến cục bộ của một hàm bên trong một hàm khác. Cả hai cũng có thể được sử dụng để lưu việc sao chép các đối tượng lớn khi được truyền làm đối số cho các hàm hoặc trả về từ các hàm, để đạt được hiệu quả. Mặc dù có những điểm tương đồng ở trên, nhưng có những điểm khác biệt sau đây giữa tham chiếu và con trỏ.

  1. Một con trỏ có thể được khai báo là void nhưng một tham chiếu thì không bao giờ.
  2. Biến con trỏ có n cấp / nhiều cấp chuyển hướng, tức là con trỏ đơn, con trỏ kép, con trỏ ba. Trong khi đó, biến tham chiếu chỉ có một / một mức chuyển hướng duy nhất.

Xem thêm về sự khác biệt giữa con trỏ và tham chiếu ở đây.

Tham chiếu không mạnh như con trỏ

  • Sau khi một tham chiếu được tạo, nó không thể được thực hiện để tham chiếu đến một đối tượng khác; nó không thể được gán lại. Điều này thường thực hiện được với con trỏ.
  • Tham chiếu không được NULL. Con trỏ thường được tạo NULL để chỉ ra rằng chúng không trỏ đến bất kỳ thứ hợp lệ nào.
  • Một tham chiếu phải được khởi tạo khi khai báo. Không có hạn chế như vậy với con trỏ.

Do các hạn chế ở trên, tham chiếu trong C++ không thể được sử dụng để triển khai cấu trúc dữ liệu như Danh sách liên kết, Cây, … Trong Java, tham chiếu không có các hạn chế nêu trên và có thể được sử dụng để triển khai tất cả các cấu trúc dữ liệu. Tham chiếu mạnh hơn trong Java là lý do chính khiến Java không cần con trỏ.

Tham chiếu an toàn và dể dùng hơn con trỏ

  • An toàn hơn: Vì các tham chiếu phải được khởi tạo, các tham chiếu “hoang dã” như con trỏ “hoang dã” không có khả năng tồn tại. Tuy nhiên, vẫn có thể có các tham chiếu không tham chiếu đến một vị trí hợp lệ (Xem câu hỏi 5 và 6 trong bài tập bên dưới)
  • Dễ sử dụng hơn: Tham chiếu không cần toán tử dereference (*) để truy cập giá trị. Chúng có thể được sử dụng giống như các biến bình thường. Toán tử (&) chỉ cần thiết tại thời điểm khai báo. Ngoài ra, các thành viên của một tham chiếu đối tượng có thể được truy cập bằng toán tử (.), Không giống như các con trỏ cần toán tử (->) để truy cập các thành viên.

Cùng với những lý do trên, có rất ít nơi như đối số của hàm tạo bản sao (copy constructor) không thể sử dụng con trỏ. Tham chiếu phải được sử dụng để chuyển đối số trong phương thức khởi tạo bản sao. Tương tự, các tham chiếu phải được sử dụng để nạp chồng một số toán tử như ++.

Bài tập tìm hiểu thêm

Dự đoán kết quả của các chương trình sau. Nếu có lỗi biên dịch, hãy sửa chúng.

Câu hỏi 1

#include<iostream>
using namespace std;
 
int &fun()
{
    static int x = 10;
    return x;
}
int main()
{
    fun() = 30;
    cout << fun();
    return 0;
}

Câu hỏi 2

#include<iostream>
using namespace std;
 
int fun(int &x)
{
    return x;
}
int main()
{
    cout << fun(10);
    return 0;
}

Câu hỏi 3

#include<iostream>
using namespace std;
 
void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}
 
int main()
{
  char *str1 = "CODE";
  char *str2 = "CUNG NHAU";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

Câu hỏi 4

#include<iostream>
using namespace std;
 
int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}

Câu hỏi 5

#include<iostream>
using namespace std;
 
int main()
{
   int *ptr = NULL;
   int &ref = *ptr;
   cout << ref;
}

Câu hỏi 6

#include<iostream>
using namespace std;
 
int &fun()
{
    int x = 10;
    return x;
}
int main()
{
    fun() = 30;
    cout << fun();
    return 0;
}

Đã đăng vào

trong

bởi

Bình luận

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *