C++ chuyển đối tượng lớp theo tham chiếu

   main() {
      int i = 10, j = 20;
      swapThemByVal(i, j);
      cout << i << "  " << j << endl;     // displays 10  20
      swapThemByRef(i, j);
      cout << i << "  " << j << endl;     // displays 20  10
      ...
   }

   void swapThemByVal(int num1, int num2) {
      int temp = num1;
      num1 = num2;
      num2 = temp;
   }

   void swapThemByRef(int& num1, int& num2) {
      int temp = num1;
      num1 = num2;
      num2 = temp;
   }

Có bốn cách truyền đối tượng cho hàm. Giả sử bạn có một lớp X và muốn chuyển nó cho một hàm fun, sau đó

Vượt qua giá trị

Điều này tạo ra một bản sao nông cục bộ của đối tượng trong phạm vi chức năng. Những thứ bạn sửa đổi ở đây sẽ không được phản ánh trong đối tượng được truyền cho nó. Ví dụ,

Tuyên ngôn

void fun(X x);

gọi

X x;
fun(x);

Vượt qua tham khảo

Điều này chuyển một tham chiếu đến đối tượng đến hàm. Những thứ bạn sửa đổi ở đây sẽ được phản ánh trong đối tượng được truyền cho nó. Không có bản sao nào của đối tượng được tạo. Ví dụ,

Tuyên ngôn

void fun(X &x);

gọi

X x;
fun(x);

Vượt qua tham chiếu const

Điều này chuyển một tham chiếu const đến đối tượng cho hàm. Bạn không thể trực tiếp sửa đổi/gán lại đối tượng ở đây (bạn có thể sử dụng các phương thức của nó để làm như vậy). Điều này hữu ích nếu bạn muốn chức năng chỉ có một bản sao chỉ đọc của đối tượng. Không có bản sao nào của đối tượng được tạo. Ví dụ,

Tuyên ngôn

void fun(X const *x);

gọi

X x;
fun(&x);

Vượt qua con trỏ const

Điều này chuyển một con trỏ const tới đối tượng cho hàm. Bạn không thể sửa đổi/gán lại con trỏ ở đây. Điều này hữu ích nếu bạn muốn hàm chỉ có địa chỉ của đối tượng này trong con trỏ. Không có bản sao của đối tượng được tạo ra. Ví dụ,

Tuyên ngôn

void fun(X *x);

gọi

X x;
fun(&x);

Vượt qua con trỏ

Điều này chuyển một con trỏ tới đối tượng tới hàm. Điều này tương tự như chuyển một tham chiếu đến đối tượng. Không có bản sao của đối tượng được tạo ra. Ví dụ,

Một lần nữa, bạn có thể thấy từ đầu ra sau rằng

X x;
fun(x);
7 đã được thay đổi bằng cách gọi hàm
#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
0

void fun(X x);
0

Truyền đối tượng theo tham chiếu

Giống như các biến không phải mảng khác, các đối tượng có thể được truyền theo tham chiếu. Ví dụ, đoạn mã chương trình sau

void fun(X x);
1

sẽ tạo ra đầu ra này

void fun(X x);
2

Việc truyền các đối tượng C++ bằng tham chiếu là cực kỳ phổ biến ngay cả khi một hàm không cần thay đổi đối tượng ban đầu trong quy trình gọi. Các đối tượng thường có thể khá lớn, vì vậy việc chuyển chúng theo giá trị có thể tốn kém về dung lượng bộ nhớ mà chúng chiếm và thời gian cần thiết để tạo một bản sao của chúng. Ngược lại, các tham chiếu (con trỏ ẩn) rất nhỏ và có kích thước cố định (thường là 4 hoặc 8 byte). Truyền đối tượng theo tham chiếu thường sẽ giúp chúng ta tiết kiệm cả bộ nhớ và thời gian

Nhược điểm duy nhất của việc truyền một đối tượng bằng tham chiếu là hàm sẽ có thể thay đổi đối tượng ban đầu đã được truyền cho nó. Đó là điều không mong muốn. Nếu một chức năng không cần thay đổi một đối tượng, lý tưởng nhất là chúng tôi muốn làm như vậy là không thể

Hơn nữa, nếu chúng ta cố truyền một đối tượng hằng bằng tham chiếu, trình biên dịch sẽ tạo ra lỗi cú pháp. Ví dụ: nếu bạn cố gắng biên dịch mã chương trình sau trên

#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
1 thành
#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
2

void fun(X x);
5

trình biên dịch sẽ tạo ra lỗi cú pháp sau

void fun(X x);
6

Thông báo lỗi này đề cập đến "vòng loại" là vòng loại

#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
3 trong phần khai báo của biến
#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
4
#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
5. Về cơ bản, trình biên dịch đang nói với chúng ta rằng "biến này mà bạn đang chuyển đến hàm
#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
6 là hằng số nhưng bạn đang chuyển nó theo cách cho phép hàm thay đổi nó. "

Tham chiếu đến các biến không đổi

Để khắc phục sự cố đó, chúng ta có thể sử dụng tham chiếu đến một biến không đổi. Điều này tạo ra một tham chiếu không thể được sử dụng để sửa đổi biến mà nó đề cập đến. Ví dụ: đoạn mã sau sẽ biên dịch mà không có bất kỳ lỗi cú pháp nào

X x;
fun(x);
1

Lưu ý rằng trình biên dịch C++ không phàn nàn nếu bạn truyền một đối tượng không cố định cho một hàm bằng cách sử dụng tham chiếu đến một đối tượng không đổi. Điều đó đơn giản có nghĩa là hàm sẽ không thể thay đổi đối tượng ban đầu trong quy trình gọi bằng cách sử dụng tham chiếu, mặc dù đối tượng ban đầu không phải là hằng số

Như một quy tắc chung

Nếu bạn muốn một hàm hoặc hàm thành viên có thể thay đổi một đối tượng C++, hãy chuyển nó bằng tham chiếu

Nếu bạn không muốn một hàm hoặc hàm thành viên có thể thay đổi một đối tượng C++, hãy chuyển nó dưới dạng tham chiếu đến một biến không đổi

Hầu hết thời gian, không có lợi ích thực sự nào khi chuyển một biến thuộc loại tích hợp sẵn như

#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
7 hoặc
#include 

using std::cout;
using std::endl;

void add_to_int(int&);

int main()
{
   int num = 5;

   cout << "In main(), num is " << num << endl;
   cout << "In main(), address of num is " << (long int) &num << endl << endl;

   add_to_int(num);

   cout << "In main(), value of num is now " << num << endl;

   return 0;
}

void add_to_int(int& num_ref)
{
   cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
   cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;

   num_ref += 10;

   cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
}
8 bằng cách sử dụng tham chiếu đến một biến không đổi. Nếu bạn không muốn một hàm sửa đổi biến ban đầu trong thói quen gọi, chỉ cần chuyển biến theo giá trị

Trả lại tài liệu tham khảo

Các hàm thành viên đôi khi cũng có thể trả về một biến hoặc đối tượng theo tham chiếu hoặc trả về một tham chiếu cho một biến không đổi. Điều này thường được thực hiện nhất nếu hàm thành viên trả về đối tượng đã gọi nó, trả về tham số hàm chính nó là tham chiếu hoặc trả về thành viên dữ liệu là đối tượng

Việc trả về một tham chiếu đến một biến cục bộ tự động được khai báo bên trong một hàm thường là một lỗi. Biến cục bộ sẽ tự động được hủy cấp phát khi chức năng kết thúc và mã gọi sẽ được để lại với tham chiếu đến một biến không còn tồn tại

Tài liệu tham khảo so với con trỏ

Tham chiếu C++ khác với con trỏ ở một số điểm cơ bản

  • Không thể tham chiếu trực tiếp đến một biến tham chiếu sau khi nó được xác định; . Điều này có thể được nhìn thấy trong đầu ra chương trình ở trên. Biến

    #include 
    
    using std::cout;
    using std::endl;
    
    void add_to_int(int&);
    
    int main()
    {
       int num = 5;
    
       cout << "In main(), num is " << num << endl;
       cout << "In main(), address of num is " << (long int) &num << endl << endl;
    
       add_to_int(num);
    
       cout << "In main(), value of num is now " << num << endl;
    
       return 0;
    }
    
    void add_to_int(int& num_ref)
    {
       cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
       cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;
    
       num_ref += 10;
    
       cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
    }
    
    9 rõ ràng là một biến khác với biến
    X x;
    fun(x);
    7, nhưng dựa trên kết quả đầu ra, chúng dường như có cùng địa chỉ, 140725741294572. Điều đó là không thể, vì hai biến khác nhau không thể chiếm cùng một địa chỉ

    Trên thực tế họ không. Sau khi biến tham chiếu

    #include 
    
    using std::cout;
    using std::endl;
    
    void add_to_int(int&);
    
    int main()
    {
       int num = 5;
    
       cout << "In main(), num is " << num << endl;
       cout << "In main(), address of num is " << (long int) &num << endl << endl;
    
       add_to_int(num);
    
       cout << "In main(), value of num is now " << num << endl;
    
       return 0;
    }
    
    void add_to_int(int& num_ref)
    {
       cout << "In add_to_int(), value of num_ref is " << num_ref << endl;
       cout << "In add_to_int(), address of num_ref is " << (long int) &num_ref << endl;
    
       num_ref += 10;
    
       cout << "In add_to_int(), value of num_ref is now " << num_ref << endl << endl;
    }
    
    9 được xác định và liên kết với
    X x;
    fun(x);
    7, bất cứ điều gì chúng ta làm với nó (chẳng hạn như cố gắng in địa chỉ của nó) đều thực sự được thực hiện với biến
    X x;
    fun(x);
    7 mà nó đề cập đến.

  • Sau khi một tham chiếu được tạo, sau này nó không thể được tạo để tham chiếu đến một biến khác. Đây là điều thường được thực hiện với con trỏ

  • Tài liệu tham khảo không thể rỗng, trong khi con trỏ có thể;

  • Tài liệu tham khảo không được phép hủy khởi tạo. Vì không thể khởi tạo lại tham chiếu nên chúng phải được khởi tạo ngay khi chúng được tạo. Cụ thể, các biến cục bộ và toàn cục phải được khởi tạo ở nơi chúng được định nghĩa và các tham chiếu là thành viên dữ liệu của một lớp phải được khởi tạo trong danh sách khởi tạo của hàm tạo của lớp. Ví dụ

    X x;
    fun(x);
    6

Thông thường, trình biên dịch C++ sẽ coi một tham chiếu là bí danh đơn giản cho biến mà nó đề cập đến (giả sử biến đó được khai báo trong cùng một phạm vi) hoặc biên dịch tham chiếu thành một con trỏ được hủy đăng ký hoàn toàn mỗi khi sử dụng tham chiếu đó.

Cú pháp cần thiết để sử dụng con trỏ có xu hướng làm cho chúng nổi bật; . Trong một khối mã C++ lớn, có thể không phải lúc nào cũng rõ ràng liệu một biến đang được truy cập được định nghĩa là biến cục bộ hay biến toàn cục hay liệu nó có phải là một tham chiếu đến một biến ở một số vị trí khác hay không, đặc biệt nếu mã trộn lẫn các tham chiếu và con trỏ. Điều đó có thể làm cho mã C++ được viết kém khó đọc và gỡ lỗi hơn

Tuy nhiên, do các thao tác được phép trên các tham chiếu rất hạn chế nên chúng dễ hiểu hơn nhiều so với con trỏ và có khả năng chống lỗi cao hơn. Mặc dù con trỏ có thể bị làm cho không hợp lệ thông qua nhiều cơ chế khác nhau, từ việc mang giá trị null đến số học ngoài giới hạn đến ép kiểu không hợp lệ để tạo ra chúng từ các số nguyên ngẫu nhiên, một tham chiếu hợp lệ trước đó chỉ trở nên không hợp lệ trong hai trường hợp

  • Nếu nó đề cập đến một biến có phân bổ tự động nằm ngoài phạm vi,
  • Nếu nó đề cập đến một đối tượng bên trong một khối bộ nhớ động đã được giải phóng

Nói chung, nếu chúng ta cần thay đổi giá trị của một đối số không phải mảng được truyền cho một hàm, chúng ta sẽ truyền nó theo tham chiếu. Tuy nhiên, có một số trường hợp bạn không có lựa chọn nào khác ngoài việc truyền đối số theo địa chỉ hoặc sử dụng con trỏ

  1. Mảng luôn được truyền theo địa chỉ. Điều đó bao gồm các chuỗi C
  2. Lưu trữ động được phân bổ bằng cách sử dụng con trỏ
  3. Đôi khi, bạn có thể muốn sử dụng hàm thư viện từ thư viện chuẩn C cũ yêu cầu đối số địa chỉ

Để được coi là một lập trình viên C++ có năng lực, cần phải hiểu về cả cơ chế chuyển qua địa chỉ và tham chiếu qua tham chiếu.

Bạn có thể chuyển một đối tượng bằng tham chiếu không?

Giá trị của một đối tượng có thể thay đổi có thể được thay đổi khi nó được truyền vào một phương thức. Không thể thay đổi giá trị của đối tượng bất biến, ngay cả khi nó được truyền một giá trị mới. "Chuyển theo giá trị" đề cập đến việc chuyển một bản sao của giá trị. “Truyền theo tham chiếu” có nghĩa là truyền tham chiếu thực của biến vào bộ nhớ .

Bạn sẽ truyền đối tượng vào hàm bằng tham chiếu như thế nào?

Truyền theo tham chiếu có nghĩa là chuyển tham chiếu của một đối số trong hàm gọi đến tham số hình thức tương ứng của hàm được gọi . Hàm được gọi có thể sửa đổi giá trị của đối số bằng cách sử dụng tham chiếu của nó được truyền vào. Ví dụ sau đây cho thấy cách các đối số được truyền bằng tham chiếu.

Chuyển qua tham chiếu có sẵn trong C không?

Truyền bằng tham chiếu đề cập đến một phương thức truyền địa chỉ của một đối số trong hàm gọi đến một tham số tương ứng trong hàm được gọi. Trong C, tham số tương ứng trong hàm được gọi phải được khai báo là kiểu con trỏ .

Truyền một đối tượng bằng cách tham chiếu có nghĩa là gì?

Xác định Vượt qua theo Tham chiếu . Theo tham chiếu có nghĩa là đối số bạn đang chuyển đến hàm là tham chiếu đến một biến đã tồn tại trong bộ nhớ chứ không phải là bản sao độc lập của biến đó. to provide an argument to a function. By reference means that the argument you're passing to the function is a reference to a variable that already exists in memory rather than an independent copy of that variable.