มีคำตอบที่ดีอยู่แล้วซึ่งครอบคลุมในเรื่องนี้ ฉันต้องการบริจาคเล็ก ๆ ด้วยการแบ่งปันตัวอย่างง่ายๆ (ซึ่งจะรวบรวม) เปรียบเทียบพฤติกรรมระหว่าง Pass-by-Reference ใน c ++ และ Pass-by-value ใน Java
คะแนนน้อย:
- คำว่า "การอ้างอิง" นั้นมีความหมายมากเกินไปโดยมีความหมายแยกกันสองแบบ ใน Java มันหมายถึงตัวชี้ แต่ในบริบทของ "Pass-by-Reference" หมายถึงการจัดการกับตัวแปรเดิมที่ผ่านมา
- Java เป็นผ่านโดยค่า Java เป็นเชื้อสายของ C (ท่ามกลางภาษาอื่น ๆ ) ก่อนหน้า C ภาษาก่อนหน้านี้หลายภาษา (แต่ไม่ใช่ทั้งหมด) เช่น FORTRAN และ COBOL รองรับ PBR แต่ C ไม่ได้ PBR อนุญาตให้ภาษาอื่นเหล่านี้สามารถเปลี่ยนแปลงตัวแปรที่ส่งผ่านภายในรูทีนย่อย เพื่อที่จะบรรลุสิ่งเดียวกัน (เช่นเปลี่ยนค่าของตัวแปรภายในฟังก์ชั่น) โปรแกรมเมอร์ C ผ่านตัวชี้ไปที่ตัวแปรในฟังก์ชั่น ภาษาที่ได้รับแรงบันดาลใจจาก C เช่น Java ยืมความคิดนี้และส่งต่อตัวชี้ไปยังวิธีการที่ C ทำยกเว้นว่า Java เรียกตัวชี้อ้างอิงของมัน อีกครั้งนี่เป็นการใช้คำว่า "การอ้างอิง" ที่แตกต่างจากใน "การอ้างอิงผ่าน"
- C ++ อนุญาตให้มีการอ้างอิงแบบอ้างอิงโดยการประกาศพารามิเตอร์อ้างอิงโดยใช้อักขระ "&" (ซึ่งเป็นอักขระตัวเดียวที่ใช้เพื่อระบุ "ที่อยู่ของตัวแปร" ในทั้ง C และ C ++) ตัวอย่างเช่นถ้าเราส่งผ่านตัวชี้โดยการอ้างอิงพารามิเตอร์และอาร์กิวเมนต์ไม่เพียงชี้ไปที่วัตถุเดียวกัน แต่เป็นตัวแปรเดียวกัน หากมีการตั้งค่าที่อยู่ที่แตกต่างกันหรือเป็นโมฆะอื่น ๆ
- ใน C ++ ตัวอย่างด้านล่างฉันผ่านตัวชี้ไปโมฆะยกเลิกสตริงโดยอ้างอิง และในตัวอย่าง Java ด้านล่างฉันผ่านการอ้างอิง Java ไปยัง String (อีกครั้งเหมือนกับตัวชี้ไปยัง String) ตามค่า สังเกตเห็นผลลัพธ์ในข้อคิดเห็น
C ++ ผ่านตัวอย่างการอ้างอิง:
using namespace std;
#include <iostream>
void change (char *&str){ // the '&' makes this a reference parameter
str = NULL;
}
int main()
{
char *str = "not Null";
change(str);
cout<<"str is " << str; // ==>str is <null>
}
Java ผ่าน "การอ้างอิง Java" โดยตัวอย่างค่า
public class ValueDemo{
public void change (String str){
str = null;
}
public static void main(String []args){
ValueDemo vd = new ValueDemo();
String str = "not null";
vd.change(str);
System.out.println("str is " + str); // ==> str is not null!!
// Note that if "str" was
// passed-by-reference, it
// WOULD BE NULL after the
// call to change().
}
}
แก้ไข
หลายคนเขียนความคิดเห็นซึ่งดูเหมือนจะบ่งบอกว่าพวกเขาไม่ได้ดูตัวอย่างของฉันหรือไม่ได้รับตัวอย่าง c ++ ไม่แน่ใจว่าการปลดการเชื่อมต่ออยู่ที่ใด แต่การเดาตัวอย่าง c ++ นั้นไม่ชัดเจน ฉันโพสต์ตัวอย่างเดียวกันเป็นปาสกาลเพราะฉันคิดว่ารหัสผ่านดูดีกว่าปาสกาล แต่ฉันคิดผิด ฉันอาจทำให้ผู้คนสับสนมากขึ้น ฉันหวังว่าไม่
ในปาสคาลพารามิเตอร์ที่ส่งผ่านโดยการอ้างอิงจะเรียกว่า "พารามิเตอร์ var" ในขั้นตอน setToNil ด้านล่างโปรดทราบคำหลัก 'var' ซึ่งอยู่ข้างหน้าพารามิเตอร์ 'ptr' เมื่อมีการชี้ถูกส่งไปยังขั้นตอนนี้ก็จะถูกส่งผ่านโดยการอ้างอิง สังเกตพฤติกรรม: เมื่อโพรซีเดอร์นี้ตั้งค่า ptr เป็นศูนย์ (นั่นเป็นภาษาปาสคาลพูดถึงค่า NULL) มันจะตั้งค่าอาร์กิวเมนต์เป็นศูนย์ - คุณไม่สามารถทำเช่นนั้นใน Java
program passByRefDemo;
type
iptr = ^integer;
var
ptr: iptr;
procedure setToNil(var ptr : iptr);
begin
ptr := nil;
end;
begin
new(ptr);
ptr^ := 10;
setToNil(ptr);
if (ptr = nil) then
writeln('ptr seems to be nil'); { ptr should be nil, so this line will run. }
end.
แก้ไข 2
ข้อความที่ตัดตอนมาบางส่วนจาก"THE Java Programming Language"โดย Ken Arnold, James Gosling (คนที่ประดิษฐ์ Java)และ David Holmes, ตอนที่ 2, ตอนที่ 2.6.5
พารามิเตอร์ทั้งหมดวิธีการจะถูกส่งผ่าน "โดยค่า" กล่าวอีกนัยหนึ่งค่าของตัวแปรพารามิเตอร์ในวิธีการคือสำเนาของผู้เรียกที่ระบุว่าเป็นข้อโต้แย้ง
เขายังคงสร้างประเด็นเดียวกันเกี่ยวกับวัตถุ . .
คุณควรทราบว่าเมื่อพารามิเตอร์การอ้างอิงวัตถุมันเป็นวัตถุอ้างอิงไม่ได้วัตถุเองที่จะถูกส่งผ่านไป "โดยค่า"
และในตอนท้ายของส่วนเดียวกันเขาได้สร้างคำสั่งที่กว้างขึ้นเกี่ยวกับจาวาเป็นเพียงการส่งผ่านค่าและไม่เคยผ่านการอ้างอิง
ภาษาโปรแกรม Java ไม่ผ่านวัตถุโดยการอ้างอิง มัน
ผ่านไปอ้างอิงวัตถุโดยค่า เนื่องจากสำเนาสองชุดของการอ้างอิงเดียวกันอ้างถึงวัตถุจริงเดียวกันการเปลี่ยนแปลงที่ทำผ่านตัวแปรอ้างอิงหนึ่งจะปรากฏให้เห็นผ่านอีกชุดหนึ่ง มีพารามิเตอร์หนึ่งตัวที่ผ่านโหมด - ส่งค่าและช่วยให้สิ่งต่าง ๆ ง่ายขึ้น
หนังสือส่วนนี้มีคำอธิบายที่ยอดเยี่ยมเกี่ยวกับการส่งผ่านพารามิเตอร์ใน Java และความแตกต่างระหว่างการอ้างอิงผ่านและการส่งต่อค่าและเป็นผู้สร้าง Java ฉันอยากจะแนะนำให้ทุกคนอ่านโดยเฉพาะถ้าคุณยังไม่มั่นใจ
ฉันคิดว่าความแตกต่างระหว่างทั้งสองรุ่นนั้นบอบบางมากและถ้าคุณไม่ได้เขียนโปรแกรมที่คุณใช้การอ้างอิงแบบอ้างอิงจริงแล้วมันก็ง่ายที่จะพลาดที่สองรุ่นนั้นแตกต่างกัน
ฉันหวังว่าสิ่งนี้จะเป็นที่ถกเถียงกัน แต่อาจจะไม่
แก้ไข 3
ฉันอาจจะหมกมุ่นอยู่กับโพสต์นี้เล็กน้อย อาจเป็นเพราะฉันรู้สึกว่าผู้ผลิต Java กระจายข้อมูลที่ผิดโดยไม่ตั้งใจ หากแทนที่จะใช้คำว่า "การอ้างอิง" สำหรับพอยน์เตอร์ที่พวกเขาใช้อย่างอื่นพูด dingleberry จะไม่มีปัญหา คุณสามารถพูดได้ว่า "Java ผ่าน dingleberries ตามคุณค่าไม่ใช่โดยการอ้างอิง" และไม่มีใครสับสน
นั่นเป็นเหตุผลที่มีเพียงผู้พัฒนา Java มีปัญหากับเรื่องนี้ พวกเขามองไปที่คำว่า "การอ้างอิง" และคิดว่าพวกเขารู้ว่ามันหมายถึงอะไรดังนั้นพวกเขาจึงไม่สนใจที่จะพิจารณาข้อโต้แย้งที่เป็นปฏิปักษ์
อย่างไรก็ตามฉันสังเกตเห็นความคิดเห็นในโพสต์เก่าซึ่งทำให้การเปรียบเทียบบอลลูนที่ฉันชอบจริงๆ มากเหลือเกินที่ฉันตัดสินใจที่จะยึดติดกันกับคลิปอาร์ตเพื่อสร้างการ์ตูนชุดหนึ่งเพื่ออธิบายประเด็น
ผ่านการอ้างอิงโดยค่า - การเปลี่ยนแปลงการอ้างอิงจะไม่สะท้อนในขอบเขตของผู้โทร แต่การเปลี่ยนแปลงของวัตถุนั้น นี่เป็นเพราะการอ้างอิงถูกคัดลอก แต่ทั้งต้นฉบับและการคัดลอกอ้างถึงวัตถุเดียวกัน
ผ่านการอ้างอิง -ไม่มีสำเนาของการอ้างอิง การอ้างอิงเดี่ยวถูกแชร์โดยทั้งผู้เรียกและฟังก์ชันที่กำลังเรียกใช้ การเปลี่ยนแปลงใด ๆ ในการอ้างอิงหรือข้อมูลของวัตถุจะปรากฏในขอบเขตของผู้โทร
แก้ไข 4
ฉันได้เห็นโพสต์ในหัวข้อนี้ซึ่งอธิบายการใช้งานพารามิเตอร์ระดับต่ำใน Java ซึ่งฉันคิดว่าดีและมีประโยชน์มากเพราะทำให้เป็นแนวคิดที่เป็นรูปธรรม อย่างไรก็ตามสำหรับฉันแล้วคำถามเกี่ยวกับพฤติกรรมที่อธิบายในสเปคภาษามากกว่าเกี่ยวกับการใช้เทคนิคของพฤติกรรม นี่คือ exerpt จากข้อกำหนดภาษา Java ส่วน 8.4.1 :
เมื่อมีการเรียกใช้เมธอดหรือคอนสตรัคเตอร์ (§15.12) ค่าของนิพจน์อาร์กิวเมนต์ที่เกิดขึ้นจริงจะเริ่มต้นตัวแปรพารามิเตอร์ที่สร้างขึ้นใหม่แต่ละชนิดที่ประกาศไว้ก่อนการเรียกใช้งานเนื้อความของเมธอดหรือคอนสตรัคเตอร์ ตัวบ่งชี้ที่ปรากฏใน DeclaratorId อาจถูกใช้เป็นชื่อง่าย ๆ ในเนื้อความของเมธอดหรือตัวสร้างเพื่ออ้างถึงพารามิเตอร์ที่เป็นทางการ
ซึ่งหมายความว่า java สร้างสำเนาของพารามิเตอร์ที่ส่งผ่านก่อนที่จะดำเนินการวิธีการ เช่นคนส่วนใหญ่ที่เรียนคอมไพเลอร์ในวิทยาลัยผมใช้"มังกรหนังสือ"ซึ่งเป็นหนังสือคอมไพเลอร์ มันมีคำอธิบายที่ดีของ "Call-by-value" และ "Call-by-Reference" ในบทที่ 1 คำอธิบาย Call-by-value ตรงกับ Java Specs อย่างแน่นอน
ย้อนกลับไปเมื่อฉันศึกษาคอมไพเลอร์ - ในยุค 90 ฉันใช้หนังสือเล่มแรกของปี 1986 ที่ชวาล่วงหน้าประมาณ 9 หรือ 10 ปี อย่างไรก็ตามฉันเพิ่งเจอสำเนาของEddition ที่ 2จากปี 2007 ซึ่งจริงๆแล้วพูดถึง Java! ส่วนที่ 1.6.6 ระบุว่า "กลไกการส่งผ่านพารามิเตอร์" อธิบายพารามิเตอร์ที่ส่งผ่านได้ค่อนข้างดี นี่คือข้อความที่ตัดตอนมาภายใต้หัวข้อ "Call-by-value" ซึ่งกล่าวถึง Java:
ใน call-by-value พารามิเตอร์จริงจะถูกประเมิน (ถ้าเป็นนิพจน์) หรือคัดลอก (หากเป็นตัวแปร) ค่าจะถูกวางในตำแหน่งที่เป็นของพารามิเตอร์ทางการที่สอดคล้องกันของขั้นตอนที่เรียกว่า วิธีนี้ใช้ใน C และ Java และเป็นตัวเลือกทั่วไปใน C ++ รวมถึงในภาษาอื่น ๆ ส่วนใหญ่