C++ chuyển theo giá trị so với chuyển theo tham chiếu

Khi viết mã phần mềm, bạn sẽ mất nhiều thời gian để xác định, đọc và thay đổi các biến. Sử dụng một biến có nghĩa là bạn sử dụng một từ mô tả trong mã chứa một số thông tin (một số, một số văn bản, một đối tượng, v.v. ). Từ mô tả này là “tiêu đề” của thông tin được lưu trữ. Ví dụ

Java

1

2

3

int tuổi của tôi = 14;

int tháng = 1;

String myName = Jon;

Các biến mang đến cho bạn sự thoải mái mà bạn không cần biết dữ liệu của mình được lưu trữ ở đâu trong bộ nhớ máy tính. Bạn xác định một biến, thực hiện các phép tính và chuyển biến từ chức năng này sang chức năng khác và hệ điều hành của bạn sẽ tự động xử lý vị trí thực tế của dữ liệu của bạn
Tuy nhiên, đôi khi rất hữu ích khi biết điều gì xảy ra đằng sau các biến. Đặc biệt là trong các ngôn ngữ cấp thấp hơn (ví dụ: Trình biên dịch mã hoặc C trong một số trường hợp), đôi khi thậm chí cần biết giá trị được lưu trữ ở đâu trong bộ nhớ. Bài viết này sẽ giải thích cách các biến được truyền giữa các hàm và giải thích cụ thể cách thức hoạt động của nó trong Java. Trong Java, có một sự khác biệt nhỏ nhưng quan trọng là truyền giá trị và truyền tham chiếu

Bài viết này sẽ sử dụng cú pháp đơn giản hóa trong các ví dụ mã, vì lý thuyết truyền giá trị có thể áp dụng cho hầu hết các ngôn ngữ lập trình

Truyền một biến

Thuật ngữ “chuyển một biến” được sử dụng khi một hàm được gọi với một biến mà bạn đã xác định trước đó. Hãy xem ví dụ sau

Java

1

2

int tuổi của tôi = 14;

calculateBirthYear(Tuổi của tôi);

Biến myAge được “truyền” cho hàm tính toánBirthYear. Hàm sau đó có thể sử dụng biến đó, ví dụ

Java

1

2

3

hàm tính năm sinh(int tuổi của bạn) {

return HIỆN TẠI_ NĂM yourAge;

}

Có hai khả năng bạn có thể chuyển biến myAge cho hàm. Các thuật ngữ “chuyển theo giá trị” và “chuyển theo tham chiếu” được sử dụng để mô tả cách các biến được truyền vào. Để làm cho nó ngắn. truyền theo giá trị có nghĩa là giá trị thực được truyền vào. Truyền theo tham chiếu có nghĩa là một số (được gọi là địa chỉ) được truyền vào để xác định nơi lưu trữ giá trị

Bộ nhớ hoạt động như thế nào

Kiến thức cơ bản về cách hoạt động của bộ nhớ và cách các biến của bạn được lưu trữ trong bộ nhớ sẽ giúp hiểu rõ hơn về chủ đề này. Vì bộ nhớ vật lý thực tế rất khó vẽ và các loại bộ nhớ khác nhau trông khác nhau (đĩa cứng so với. RAM chẳng hạn), thật hữu ích khi có một cách trừu tượng và đơn giản để hình dung bộ nhớ trông như thế nào

Bài viết này sử dụng cách tiếp cận đơn giản và dễ hiểu để giải thích cách các giá trị được lưu trữ trong bộ nhớ. Đối với hầu hết các mục đích sử dụng (đặc biệt là đối với người mới bắt đầu lập trình), việc hiểu các khái niệm là đủ. Tuy nhiên, một khi đã có kinh nghiệm lập trình, sẽ rất hữu ích khi biết thêm về các loại bộ nhớ và vùng bộ nhớ khác nhau (ổ cứng, RAM, heap, stack, v.v. )

C++ chuyển theo giá trị so với chuyển theo tham chiếu
Các khối trong bộ nhớ với địa chỉ của chúng và hai giá trị ví dụ

Để làm cho nó đơn giản, hãy coi bộ nhớ là nhiều khối nằm cạnh nhau. Mỗi khối có một số (địa chỉ bộ nhớ). Nếu bạn định nghĩa một biến trong mã của mình, giá trị của biến đó sẽ được lưu trữ ở đâu đó trong bộ nhớ (hệ điều hành của bạn sẽ tự động quyết định nơi lưu trữ tốt nhất). Hình minh họa bên phải cho thấy một phần ký ức. Các số màu xám ở đầu mỗi khối hiển thị địa chỉ của khối trong bộ nhớ, các số màu ở dưới cùng hiển thị các giá trị được lưu trong bộ nhớ

Lấy lại các ví dụ trước, các biến tuổi của tôitháng are defined in your code, and they will be stored in memory as shown in the illustration on the right. As example, the value of myAge được lưu trữ tại địa chỉ 106 và giá trị của tháng is stored at the address 113.

Vượt qua giá trị

Truyền theo giá trị có nghĩa là giá trị của tham số hàm được sao chép vào một vị trí khác trong bộ nhớ của bạn và khi truy cập hoặc sửa đổi biến trong hàm của bạn, chỉ bản sao được truy cập/sửa đổi và giá trị ban đầu không bị ảnh hưởng. Truyền theo giá trị là cách các giá trị của bạn được truyền trong hầu hết thời gian
Ví dụ sau đây cho thấy một biến được truyền theo giá trị

Java

1

2

int tuổi của tôi = 14;

calculateBirthYear(Tuổi của tôi);

C++ chuyển theo giá trị so với chuyển theo tham chiếu
Giá trị trong bộ nhớ được sao chép sang một vị trí khác để sử dụng trong hàm

Ngay sau khi phần mềm của bạn bắt đầu xử lý hàm tính toánBirthYear, giá trị myAge sẽ được sao chép vào một nơi khác trong bộ nhớ máy tính của bạn. Để làm cho điều này rõ ràng hơn, biến trong hàm có tên là age trong ví dụ này.

Java

1

2

3

4

hàm calculateBirthYear(int tuổi) {

int năm sinh = HIỆN TẠI_YEAR age;

return năm sinh;

}

Mọi thứ hiện đang xảy ra với age không ảnh hưởng đến giá trị của myAge (which is outside of calculateBirthYear‘s function scope) at all.

Vậy làm thế nào để bạn sửa đổi/cập nhật một biến bên ngoài chức năng của mình? . Điều này có nghĩa là chỉ có thể thay đổi một giá trị bên ngoài hàm

Một chức năng rất đơn giản để trình diễn

Java

1

2

3

function increaseAge(int age) {

return tuổi + 1;

}

Trong mã của bạn, biến nguồn được cập nhật với giá trị trả về của hàm

Java

1

2

int tuổi của tôi = 14;

tuổi của tôi = tăngTuổi(tuổi của tôi);

C++ chuyển theo giá trị so với chuyển theo tham chiếu
Bộ nhớ sau khi cập nhật biến

Biến myAge hiện giữ giá trị 15 (xem hình minh họa bên phải).

Chuyển qua tham chiếu

Truyền theo tham chiếu có nghĩa là địa chỉ bộ nhớ của biến (một con trỏ tới vị trí bộ nhớ) được truyền cho hàm. Điều này không giống như truyền theo giá trị, trong đó giá trị của một biến được truyền vào. Trong các ví dụ, địa chỉ bộ nhớ của myAge là 106. Khi chuyển myAge cho hàm gainAgeByRef, biến được sử dụng trong hàm ( age in this example) still points to the same memory address as the original variable myAge (Hint: the & symbol in front of the function parameter is used in many programming languages to get the reference/pointer of a variable).

Java

1

2

3

hàm increaseAgeByRef(int &age) {

*tuổi = *tuổi + 1;

}

Khi gọi hàm, giá trị của myAge được thay đổi trực tiếp thông qua tham chiếu của nó.

Java

1

2

int tuổi của tôi = 14;

increaseAgeByRef(tuổi của tôi);

C++ chuyển theo giá trị so với chuyển theo tham chiếu
Bộ nhớ sau khi thay đổi biến thông qua tham chiếu của nó

Giá trị của myAge hiện là 15.

Với việc chuyển qua tham chiếu, giờ đây có thể thay đổi nhiều hơn một biến được cung cấp trong tham số hàm. Ngoài ra, một biến có thể được trả về thông qua giá trị trả về của hàm (thường là trạng thái, thành công/thất bại hoặc biến quan trọng nhất). Ví dụ

Java

1

2

3

4

5

6

function doSomething(int &count1, int &count2, int &count3) {

*count1 = *count1 + 1;

*count2 = *count2 + 1;

*count3 = *count3 + 1;

return true;

}

(Gợi ý. Ký hiệu & trả về địa chỉ của một biến, ký hiệu * trả về những gì được lưu trữ tại giá trị địa chỉ, “đảo ngược” ký hiệu &)

Để đưa ra quyết định sử dụng truyền theo tham chiếu hay truyền theo giá trị, có hai quy tắc chung đơn giản

  • Nếu một chức năng sẽ trả về một giá trị duy nhất. sử dụng vượt qua giá trị
  • Nếu một hàm sẽ trả về hai hoặc nhiều giá trị riêng biệt. sử dụng vượt qua bằng cách tham khảo

Truyền qua tham chiếu thường có thể tránh được bằng cách sử dụng mảng hoặc cấu trúc (hoặc đối tượng trong ngôn ngữ cấp cao)

Java chuyển theo giá trị

Java là một ngôn ngữ lập trình bậc cao. Điều này có nghĩa là trong những trường hợp bình thường, bạn không phải lo lắng về những gì xảy ra trong bộ nhớ (và các chủ đề trước không cần thiết cho lập trình Java, nhưng rất hữu ích cho sự hiểu biết chung). Chủ yếu là do nguyên thủy (như int, double, v.v. ) luôn được truyền theo giá trị và không có toán tử & trong Java để lấy địa chỉ của chúng. Hơn nữa, các biến thường được “đóng gói” trong một đối tượng và các biến đối tượng được truyền xung quanh. Về chủ đề truyền biến trong Java, có một câu lệnh đơn giản

Java LUÔN LUÔN chuyển theo giá trị

Nếu bạn gán một giá trị (hoặc đối tượng) cho một biến tham số trong một phương thức, thì biến bên ngoài phương thức đó vẫn có cùng một giá trị (hoặc đối tượng). Đây là một ví dụ đơn giản để kiểm tra điều này

Java

1

2

3

4

5

6

7

8

9

10

11

12

lớp công khai Lớp kiểm tra {

công khai Lớp kiểm tra() {

SomeObject someObject = new SomeObject(object 1);

Hệ thống. ra. println(someObject);

testMethod(someObject);

Hệ thống. ra. println(someObject);

}

 

công khai testMethod(SomeObject đối tượng) {

đối tượng = mới Một số đối tượng(object 2);

}

}

Khi chạy mã này, đầu ra dữ liệu đối tượng sẽ giống nhau cả hai lần mặc dù biến được sửa đổi trong phương thức testMethod. Thử nghiệm đơn giản này cho thấy rằng biến someObject được truyền theo giá trị chứ không phải theo tham chiếu. Nó cho thấy đối tượng biến phải ở một vị trí khác trong bộ nhớ, bởi vì khi giá trị của nó bị thay đổi, giá trị của biến nguồn someObject không thay đổi.
Một đối số sai được sử dụng thường xuyên để giải thích rằng Java truyền qua tham chiếu là đối tượng có thể được sửa đổi trong phương thức, ví dụ như với

Java

1

2

3

công khai testMethod(SomeObject đối tượng) {

đối tượng. setName(đối tượng 3);

}

Tuy nhiên, điều này thay đổi một thuộc tính đối tượng chứ không phải giá trị của đối tượng biến. Giá trị của đối tượng biến vẫn giữ nguyên

Biến đối tượng Java

Để tiếp tục khám phá chủ đề, các biến đối tượng phải được xem xét chi tiết hơn. Một biến đối tượng sẽ giống như

Java

1

SomeObject someObject = new SomeObject(object 1);

C++ chuyển theo giá trị so với chuyển theo tham chiếu
Biến đối tượng Java

(Điều tương tự áp dụng cho bất kỳ đối tượng nào khác, chẳng hạn như Chuỗi). Chi tiết ở đây là biến someObject không chứa chính đối tượng, giá trị của biến là con trỏ tới đối tượng. Ví dụ như trong hình minh họa bên phải (sử dụng một khối bộ nhớ cho đơn giản), giá trị của someObject là 121. Điều này có thể gây nhầm lẫn vì khi gọi một phương thức như

Java

1

testMethod2(someObject);

số 121 được truyền cho phương thức và có vẻ như "truyền theo tham chiếu". Tuy nhiên, giá trị 121 được lưu trữ trong biến someObject được truyền vào và không phải là con trỏ tới nơi lưu trữ giá trị 121 trong bộ nhớ. Một ví dụ triển khai.

Java

1

2

3

4

5

công khai testMethod2(SomeObject obj) {

obj. setName(o1);

obj = new SomeObject(object 2);

obj. setName(o2);

}

Trên dòng đầu tiên, biến được truyền vào là đối tượng “đối tượng 1” từ someObject và tên của nó được đổi thành “o1”. Sau đó, một đối tượng mới “đối tượng 2” được gán cho biến obj (đổi màu cho rõ ràng) và trên dòng tiếp theo, tên đối tượng mới được đặt thành “o2”. Biến someObject vẫn có tên “o1”.
Điều này mang đến một tuyên bố khác ngoài tuyên bố trước đó

Trong Java, tham chiếu đối tượng (con trỏ) được truyền theo giá trị

 

Điều này đóng chủ đề về truyền biến trong Java. Khi làm việc với con trỏ, thường cần phải suy nghĩ rất trừu tượng để hiểu logic chương trình (và những điều đáng ngạc nhiên có thể xảy ra khi sửa đổi con trỏ thay vì giá trị). Đây là lý do tại sao các ngôn ngữ lập trình cấp cao hơn cố gắng che giấu sự phức tạp đó để cho phép bạn tập trung vào những thứ khác