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ụ Show 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 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ếnThuậ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 Java1 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ụ Java1 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àoKiế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. ) Để 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ôi và thá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 1 2 int tuổi của tôi = 14; calculateBirthYear(Tuổi của tôi); 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. 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 Java1 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 Java1 2 int tuổi của tôi = 14; tuổi của tôi = tăngTuổi(tuổi của tôi); Biến myAge hiện giữ giá trị 15 (xem hình minh họa bên phải). Chuyển qua tham chiếuTruyề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). Java1 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ó. 1 2 int tuổi của tôi = 14; increaseAgeByRef(tuổi của tôi); 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ụ Java1 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
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
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 Java1 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. 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ư Java1 SomeObject someObject = new SomeObject(“object 1”); (Đ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ư Java1 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. Java1 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 đó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 |