ความแตกต่างระหว่างการส่งต่อโดยอ้างอิงกับการผ่านตามมูลค่าคืออะไร


562

อะไรคือความแตกต่างระหว่าง

  1. พารามิเตอร์ที่ส่งผ่านโดยการอ้างอิง
  2. พารามิเตอร์ผ่านค่าหรือไม่

กรุณายกตัวอย่างให้ฉันหน่อยได้ไหม



1
หากคุณไม่ทราบว่าที่อยู่หรือค่าคืออะไรให้ดูที่นี่
ฮันนี่

คำตอบ:


1079

แรกและสำคัญที่สุด"การผ่านค่าเมื่อเทียบกับการส่งผ่านโดยการอ้างอิง" ความแตกต่างตามที่กำหนดไว้ในทฤษฎี CS อยู่ในขณะนี้ล้าสมัยเพราะเทคนิคที่กำหนดไว้เดิมเป็น "ผ่านการอ้างอิง" ได้ลดลงตั้งแต่ออกจากความโปรดปรานและไม่ค่อยมีใครใช้ในขณะนี้ 1

ภาษาที่ใหม่กว่า2มีแนวโน้มที่จะใช้เทคนิคที่ต่างกัน (แต่คล้ายกัน) เพื่อให้ได้เอฟเฟกต์แบบเดียวกัน (ดูด้านล่าง) ซึ่งเป็นแหล่งที่มาหลักของความสับสน

แหล่งที่มาของความสับสนที่สองคือข้อเท็จจริงที่ว่า"การส่งต่อโดยอ้างอิง", "อ้างอิง" มีความหมายแคบกว่าคำว่า "อ้างอิง" ทั่วไป (เนื่องจากวลีมีมาก่อน)


ตอนนี้นิยามที่แท้จริงคือ:

  • เมื่อพารามิเตอร์ถูกส่งผ่านโดยการอ้างอิงผู้เรียกและผู้รับใช้จะใช้ตัวแปรเดียวกันสำหรับพารามิเตอร์ หากผู้รับการแก้ไขพารามิเตอร์ตัวแปรผลกระทบที่สามารถมองเห็นได้กับตัวแปรของผู้โทร

  • เมื่อพารามิเตอร์ถูกส่งผ่านโดยค่าผู้เรียกและผู้รับมีสองตัวแปรอิสระที่มีค่าเดียวกัน หากผู้ติดตามปรับเปลี่ยนตัวแปรพารามิเตอร์จะไม่เห็นเอฟเฟกต์ผู้โทร

สิ่งที่ควรทราบในคำจำกัดความนี้คือ:

  • "Variable" ที่นี่หมายถึงตัวแปรของผู้โทร (ท้องถิ่นหรือทั่วโลก) - นั่นคือถ้าฉันส่งตัวแปรท้องถิ่นโดยการอ้างอิงและกำหนดให้กับมันฉันจะเปลี่ยนตัวแปรของผู้โทรได้เองเช่นไม่ใช่สิ่งที่ชี้ไปหากเป็นตัวชี้ .

    • ตอนนี้ถือว่าเป็นการปฏิบัติที่ไม่ดี (เป็นการพึ่งพาโดยนัย) ด้วยเหตุนี้ภาษาที่ใหม่กว่าทั้งหมดจึงเป็นภาษาเฉพาะหรือเกือบทั้งหมดโดยผ่านค่า ตอนนี้การใช้ Pass-by-reference ส่วนใหญ่ใช้ในรูปแบบของ "เอาท์พุท / อาร์กิวเมนต์เข้า" ในภาษาที่ฟังก์ชันไม่สามารถส่งคืนมากกว่าหนึ่งค่า
  • ความหมายของ "การอ้างอิง" ใน "ผ่านการอ้างอิง" ความแตกต่างกับคำว่า "อ้างอิง" ทั่วไปคือคำว่า"อ้างอิง" นี้เป็นการชั่วคราวและโดยนัย สิ่งที่ผู้ได้รับโดยทั่วไปคือ"ตัวแปร" ที่เป็น "เหมือนกัน" กับต้นฉบับ ความสำเร็จของเอฟเฟ็กต์นี้โดยเฉพาะนั้นมีความเกี่ยวข้องเพียงใด (เช่นภาษาอาจเปิดเผยรายละเอียดการใช้งานบางอย่าง - ที่อยู่พอยน์เตอร์การอ้างอิง - นี่คือสิ่งที่ไม่เกี่ยวข้องทั้งหมดหากผลกระทบสุทธิคือสิ่งนี้


ตอนนี้ในภาษาสมัยใหม่ตัวแปรมีแนวโน้มที่จะเป็น "ประเภทอ้างอิง" (แนวคิดอื่นที่คิดค้นภายหลัง "ส่งต่อโดยอ้างอิง" และได้รับแรงบันดาลใจจากมัน) นั่นคือข้อมูลวัตถุจริงจะถูกจัดเก็บแยกจากที่อื่น (ปกติบนกอง) และ มีเพียง "การอ้างอิง" ที่มีอยู่ในตัวแปรและส่งผ่านเป็นพารามิเตอร์ 3

การส่งการอ้างอิงดังกล่าวตกอยู่ภายใต้การส่งผ่านตามค่าเนื่องจากค่าของตัวแปรเป็นเทคนิคการอ้างอิงเองไม่ใช่วัตถุที่อ้างอิง อย่างไรก็ตามผลกระทบสุทธิในโปรแกรมสามารถเหมือนกับค่า pass-by-value หรือ pass-by-reference:

  • หากการอ้างอิงถูกนำมาจากตัวแปรของผู้โทรและส่งผ่านเป็นอาร์กิวเมนต์สิ่งนี้จะมีผลเช่นเดียวกับการส่งต่อโดยการอ้างอิง: ถ้าวัตถุที่อ้างอิงถูกเปลี่ยนแปลงใน callee ผู้โทรจะเห็นการเปลี่ยนแปลง
    • อย่างไรก็ตามหากตัวแปรที่ถือการอ้างอิงนี้ถูกกำหนดค่าใหม่มันจะหยุดชี้ไปที่วัตถุนั้นดังนั้นการดำเนินการเพิ่มเติมเกี่ยวกับตัวแปรนี้จะส่งผลกระทบต่อสิ่งที่มันชี้ไปในขณะนี้
  • หากต้องการให้เอฟเฟกต์เหมือนกับ Pass-by-value จะมีการทำสำเนาวัตถุในบางจุด ตัวเลือกรวมถึง:
    • ผู้โทรสามารถทำสำเนาแบบส่วนตัวก่อนการโทรและให้ผู้อ้างอิงได้รับการอ้างอิงถึงสิ่งนั้นแทน
    • ในบางภาษาบางชนิดของวัตถุเป็น "ไม่เปลี่ยนรูป": การดำเนินการใด ๆ กับพวกเขาที่ดูเหมือนจะเปลี่ยนค่าจริงสร้างวัตถุใหม่อย่างสมบูรณ์โดยไม่ส่งผลกระทบต่อคนเดิม ดังนั้นการส่งวัตถุประเภทนี้เป็นอาร์กิวเมนต์มักจะมีผลกระทบของการส่งผ่านตามตัวอักษร: สำเนาสำหรับ callee จะทำโดยอัตโนมัติหากและเมื่อต้องการการเปลี่ยนแปลงและวัตถุของผู้โทรจะไม่ได้รับผลกระทบ
      • ในภาษาที่ใช้งานได้วัตถุทั้งหมดจะไม่เปลี่ยนรูป

อย่างที่คุณอาจเห็นเทคนิคของคู่นี้เกือบจะเหมือนกันกับในคำจำกัดความเฉพาะกับระดับของการเปลี่ยนทิศทาง: เพียงแค่แทนที่ "ตัวแปร" ด้วย "วัตถุอ้างอิง"

ไม่มีชื่อที่ตกลงกันไว้สำหรับพวกเขาซึ่งนำไปสู่คำอธิบายที่บิดเบี้ยวเช่น "การเรียกตามค่าที่ค่าเป็นการอ้างอิง" ในปี 1975 บาร์บาร่าลิสคอฟแนะนำคำว่า "การโทรหาโดยการแชร์วัตถุ " (หรือบางครั้งก็เป็นเพียงแค่ ยิ่งกว่านั้นวลีเหล่านี้ไม่ได้วาดคู่ขนานกับคู่ดั้งเดิม ไม่น่าแปลกใจที่คำศัพท์เก่า ๆ ถูกนำกลับมาใช้ใหม่หากไม่มีสิ่งใดที่ดีกว่านำไปสู่ความสับสน 4


หมายเหตุ : เป็นเวลานานคำตอบนี้เคยพูดว่า:

สมมติว่าฉันต้องการแบ่งปันหน้าเว็บกับคุณ ถ้าฉันบอก URL ฉันกำลังผ่านการอ้างอิง คุณสามารถใช้ URL นั้นเพื่อดูหน้าเว็บเดียวกับที่ฉันเห็น หากหน้านั้นมีการเปลี่ยนแปลงเราทั้งคู่จะเห็นการเปลี่ยนแปลง หากคุณลบ URL สิ่งที่คุณทำอยู่นั้นกำลังทำลายการอ้างอิงของคุณไปยังหน้านั้น - คุณไม่ได้ลบหน้าจริงออกไป

ถ้าฉันพิมพ์หน้านี้และให้ผลงานพิมพ์ออกมาฉันก็ผ่านคุณค่าไป หน้าของคุณเป็นสำเนาดั้งเดิมที่ไม่ได้เชื่อมต่อ คุณจะไม่เห็นการเปลี่ยนแปลงใด ๆ ที่ตามมาและการเปลี่ยนแปลงใด ๆ ที่คุณทำ (เช่นการขีดเขียนในงานพิมพ์ของคุณ) จะไม่ปรากฏในหน้าต้นฉบับ หากคุณทำลายงานพิมพ์คุณได้ทำลายสำเนาของวัตถุจริงๆ - แต่หน้าเว็บดั้งเดิมยังคงไม่เปลี่ยนแปลง

สิ่งนี้ถูกต้องส่วนใหญ่ยกเว้นความหมายที่แคบกว่าของ "การอ้างอิง" - มันเป็นทั้งชั่วคราวและโดยปริยาย (ไม่จำเป็นต้องมี แต่ชัดเจนและ / หรือถาวรเป็นคุณลักษณะเพิ่มเติมไม่ใช่ส่วนหนึ่งของความหมายแบบส่งต่อโดยอ้างอิง ตามที่อธิบายข้างต้น) การเปรียบเทียบที่ใกล้กว่าจะให้สำเนาเอกสารกับคุณและเชิญชวนให้คุณทำงานกับต้นฉบับ


1 ยกเว้นว่าคุณกำลังเขียนโปรแกรมใน Fortran หรือ Visual Basic ไม่ใช่พฤติกรรมเริ่มต้นและในภาษาส่วนใหญ่ที่ใช้งานในปัจจุบันการโทรโดยอ้างอิงจริงไม่สามารถทำได้

2 จำนวนที่มากกว่าของคนที่มีอายุมากกว่าสนับสนุนเช่นกัน

3 ในหลายภาษาที่ทันสมัยทุกประเภทเป็นประเภทอ้างอิง วิธีการนี้ได้รับการริเริ่มโดย CLU ภาษาในปี 1975 และได้รับการรับรองจากภาษาอื่น ๆ มากมายรวมถึง Python และ Ruby และภาษาอื่น ๆ อีกมากมายใช้วิธีไฮบริดโดยที่บางประเภทคือ "ประเภทค่า" และอื่น ๆ คือ "ประเภทอ้างอิง" - ในบรรดาภาษาเหล่านั้น ได้แก่ C #, Java และ JavaScript

4 มีอะไรที่ไม่ดีคือมีการรีไซเคิลเป็นคำเก่าที่เหมาะสมต่อ seแต่หนึ่งอย่างใดให้ชัดเจนซึ่งหมายถูกนำมาใช้ในแต่ละครั้ง การไม่ทำเช่นนั้นเป็นสิ่งที่ทำให้เกิดความสับสน


โดยส่วนตัวแล้วฉันจะใช้คำว่า "ใหม่"หรือ"อ้อม" ผ่านค่า / การส่งต่ออ้างอิงสำหรับเทคนิคใหม่
ivan_pozdeev

คำนิยาม "ของแท้" ที่คุณให้ไม่ใช่ความหมายที่กำหนดในหลักสูตรการเขียนโปรแกรมเบื้องต้นเกือบทุกหลักสูตร Google ผ่านการอ้างอิงอะไรและคุณจะไม่ได้รับคำตอบนั้น คำจำกัดความที่แท้จริงที่คุณให้นั้นเป็นการใช้คำอ้างอิงในทางที่ผิดเช่นเมื่อคุณทำตามคำจำกัดความที่คุณใช้นามแฝงไม่ใช่การอ้างอิง: คุณมีสองตัวแปรที่เป็นตัวแปรเดียวกันจริง ๆ นั่นคือนามแฝงและไม่อ้างอิง คำจำกัดความที่แท้จริงของคุณทำให้เกิดความสับสนอย่างมากโดยไม่มีเหตุผล เพียงพูดผ่านการอ้างอิงหมายถึงการผ่านที่อยู่ มันสมเหตุสมผลและจะหลีกเลี่ยงความสับสนที่ไม่มีจุดหมาย
YungGun

@YungGun 1) โปรดระบุลิงก์ไปยัง "คำจำกัดความที่กำหนดในเกือบทุกหลักสูตรการเขียนโปรแกรมเบื้องต้น" โปรดทราบด้วยว่าสิ่งนี้มีจุดมุ่งหมายเพื่อความชัดเจนในความเป็นจริงของวันนี้ไม่ใช่ในความเป็นจริงของทศวรรษหรือสามปีที่ผ่านมาเมื่อเขียนหลักสูตร CS บางหลักสูตร 2) "ที่อยู่" ไม่สามารถใช้ในการกำหนดเพราะมันเป็นนามธรรมโดยเจตนาจากการใช้งานที่เป็นไปได้ เช่นบางภาษา (Fortran) ไม่มีพอยน์เตอร์ พวกเขายังแตกต่างกันว่าพวกเขาเปิดเผยที่อยู่ดิบให้กับผู้ใช้ (VB ไม่); นอกจากนี้ยังไม่จำเป็นต้องเป็นที่อยู่หน่วยความจำดิบทุกอย่างที่อนุญาตให้เชื่อมโยงกับตัวแปรนั้นทำได้
ivan_pozdeev

@ivan_podeev ไม่มีลิงก์ขออภัย ฉันพูดว่า "เกือบทุกหลักสูตรเบื้องต้น" เพราะโดยส่วนตัวแล้วฉันไปโรงเรียนและเอา bootcamps การเขียนโปรแกรมเกินไปที่สอนฉันว่า หลักสูตรเหล่านี้ทันสมัย ​​(น้อยกว่า 5 ปีที่แล้ว) "ที่อยู่ดิบ" เป็นคำพ้องความหมายสำหรับ "ตัวชี้" ... คุณอาจถูกต้องทางเทคนิค (ตามลิงค์ของเชอร์รี่ที่เลือกไว้) แต่ภาษาที่คุณใช้นั้นใช้ไม่ได้และสร้างความสับสนให้กับโปรแกรมเมอร์ส่วนใหญ่ หากคุณต้องการความคิดแบบเต็มของฉันฉันได้เขียนโพสต์บล็อกคำ 3500: medium.com/@isaaccway228/ …
YungGun

@YungGun "ยาวเกินไปไม่อ่าน" ภาพรวมแสดงให้เห็นถึงความสับสนที่ระบุไว้ในคำตอบ ผ่านการอ้างอิงเป็นเทคนิคนามธรรมไม่เชื่อเรื่องพระเจ้าเพื่อการดำเนินการ มันไม่สำคัญว่าจะถูกส่งผ่านใต้ฝากระโปรงตรงหรือเปล่า
ivan_pozdeev

150

มันเป็นวิธีการส่งผ่านข้อโต้แย้งไปยังฟังก์ชั่น การส่งต่อโดยการอ้างอิงหมายถึงพารามิเตอร์ที่เรียกว่าฟังก์ชั่น 'จะเหมือนกับอาร์กิวเมนต์ที่ส่งผ่านของผู้โทร (ไม่ใช่ค่า แต่เป็นข้อมูลเฉพาะตัว - ตัวแปรนั้น) Pass by value หมายถึงพารามิเตอร์ที่เรียกว่าฟังก์ชั่น 'จะเป็นสำเนาของอาร์กิวเมนต์ที่ส่งผ่านของผู้โทร ค่าจะเหมือนกัน แต่ข้อมูลเฉพาะตัว - ตัวแปร - นั้นแตกต่างกัน ดังนั้นการเปลี่ยนแปลงพารามิเตอร์ที่ทำโดยฟังก์ชั่นที่เรียกว่าในกรณีหนึ่งการเปลี่ยนแปลงอาร์กิวเมนต์ผ่านและในกรณีอื่นเพียงแค่เปลี่ยนค่าของพารามิเตอร์ในฟังก์ชั่นที่เรียกว่า (ซึ่งเป็นเพียงการคัดลอก) รีบด่วน:

  • Java รองรับเฉพาะการส่งผ่านค่า คัดลอกอาร์กิวเมนต์ทุกครั้งแม้ว่าเมื่อคัดลอกการอ้างอิงไปยังวัตถุพารามิเตอร์ในฟังก์ชั่นที่เรียกว่าจะชี้ไปที่วัตถุเดียวกันและการเปลี่ยนแปลงไปยังวัตถุนั้นจะเห็นในผู้โทร เนื่องจากอาจทำให้สับสนนี่คือสิ่งที่ Jon Skeet ได้พูดเกี่ยวกับเรื่องนี้
  • C # รองรับ pass by value และ pass by reference (คีย์เวิร์ดที่refใช้ที่ caller และ function ที่เรียก) จอนสกีตนอกจากนี้ยังมีคำอธิบายที่ดีของนี้ที่นี่
  • C ++ รองรับการส่งผ่านค่าและผ่านการอ้างอิง (ประเภทพารามิเตอร์การอ้างอิงที่ใช้ในฟังก์ชั่นที่เรียกว่า) คุณจะพบคำอธิบายด้านล่างนี้

รหัส

เนื่องจากภาษาของฉันคือ C ++ ฉันจะใช้ที่นี่

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

และตัวอย่างใน Java จะไม่เจ็บ:

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

วิกิพีเดีย

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

ผู้ชายคนนี้สวยมากเล็บมัน:

http://javadude.com/articles/passbyvalue.htm


9
ทำไม downvote หากมีสิ่งใดผิดปกติหรือทำให้เกิดความเข้าใจผิดโปรดแสดงความคิดเห็น
Johannes Schaub - litb

1
ไม่ใช่การลงคะแนนของฉัน แต่เป็นเพียงประเด็นเล็ก ๆ น้อย ๆ (คุณรู้ไหมว่าเป็นเรื่องที่เกิดขึ้นในการโต้วาทีของประธานาธิบดี) ฉันพูดได้ว่ามันเป็น "ยุทธวิธี" มากกว่ากลยุทธ์ "มากกว่า"
harpo

28
+1 เพื่อความสมบูรณ์ ไม่ต้องกังวลกับ downvotes ผู้คนทำมันด้วยเหตุผลแปลก ๆ ในคำถามความคิดเห็นเกี่ยวกับเครื่องคิดเลขทุกคนถูก downvoted โดยคนที่ไม่คิดว่าโปรแกรมเมอร์ควรใช้เครื่องคิดเลข! อย่างไรก็ตามฉันคิดว่าคำตอบของคุณดีมาก
Mark Brittingham

1
ลิงก์ไปยังคำอธิบายของ Skeet ใช้งานไม่ได้
โปรแกรมเมอร์ที่มุ่งเน้นเงิน

ลิงก์ไปยังคำอธิบายของ Skeet ยังใช้งานไม่ได้
Rokit

85

คำตอบจำนวนมากที่นี่ (และโดยเฉพาะคำตอบที่ upvoted มากที่สุด) นั้นไม่ถูกต้องเนื่องจากพวกเขาเข้าใจผิดว่า "การโทรโดยการอ้างอิง" หมายถึงอะไรจริงๆ นี่คือความพยายามของฉันที่จะตั้งเรื่องตรง

TL; DR

ในแง่ง่ายที่สุด:

  • การเรียกตามค่าหมายความว่าคุณส่งค่าเป็นอาร์กิวเมนต์ของฟังก์ชัน
  • เรียกโดยอ้างอิงหมายความว่าคุณผ่านตัวแปรเป็นอาร์กิวเมนต์ฟังก์ชั่น

ในแง่คำเปรียบเทียบ:

  • โดยค่าโทรเป็นที่ที่ผมเขียนลงบางสิ่งบางอย่างในชิ้นส่วนของกระดาษและมอบให้คุณ อาจเป็น URL แต่อาจเป็นสำเนาของสงครามและสันติภาพที่สมบูรณ์ ไม่ว่าสิ่งที่มันคือมันบนแผ่นกระดาษซึ่งผมได้ให้กับคุณและอื่น ๆ ตอนนี้ก็เป็นได้อย่างมีประสิทธิภาพชิ้นส่วนของกระดาษ ตอนนี้คุณมีอิสระที่จะเขียนบนกระดาษแผ่นนั้นหรือใช้กระดาษแผ่นนั้นเพื่อค้นหาบางสิ่งบางอย่างและเล่นกับมันไม่ว่าอะไรก็ตาม
  • โทรโดยอ้างอิงคือเมื่อฉันให้คุณโน๊ตบุ๊คของฉันที่มีบางสิ่งบางอย่างที่เขียนลงในนั้น คุณอาจเขียนในสมุดบันทึกของฉัน (บางทีฉันต้องการให้คุณบางทีฉันไม่ได้) และหลังจากนั้นฉันเก็บสมุดบันทึกของฉันด้วยสิ่งที่คุณเขียนไว้ที่นั่น นอกจากนี้หากสิ่งที่คุณหรือฉันเขียนมีข้อมูลเกี่ยวกับวิธีการค้นหาบางสิ่งบางอย่างอื่นคุณหรือฉันสามารถไปที่นั่นและทำซอกับข้อมูลนั้น

อะไรคือ "โทรตามมูลค่า" และ "การเรียกร้องโดยอ้างอิง" ไม่ได้หมายถึง

โปรดทราบว่าแนวคิดทั้งสองนี้เป็นอิสระอย่างสมบูรณ์และ orthogonal จากแนวคิดของประเภทการอ้างอิง (ซึ่งใน Java เป็นทุกประเภทที่เป็นชนิดย่อยของObjectและใน C # ทุกclassประเภท) หรือแนวคิดของประเภทตัวชี้เช่นใน C (ซึ่งเทียบเท่ากับความหมาย ถึง "ประเภทอ้างอิง" ของ Java เพียง แต่มีไวยากรณ์ที่แตกต่างกัน)

แนวคิดของประเภทการอ้างอิงสอดคล้องกับ URL: มันเป็นทั้งข้อมูลตัวหนึ่งและเป็นข้อมูลอ้างอิง ( ตัวชี้ถ้าคุณต้องการ) กับข้อมูลอื่น คุณสามารถมีสำเนา URL จำนวนมากในที่ต่าง ๆ และพวกเขาจะไม่เปลี่ยนแปลงสิ่งที่เว็บไซต์ทั้งหมดเชื่อมโยงไปถึง หากเว็บไซต์นั้นได้รับการปรับปรุงการคัดลอก URL ทุกครั้งจะยังคงนำไปสู่ข้อมูลที่อัปเดต ในทางกลับกันการเปลี่ยน URL ในที่เดียวจะไม่มีผลกับสำเนาที่เป็นลายลักษณ์อักษรอื่น ๆ ของ URL

โปรดทราบว่า C ++ มีแนวคิดเกี่ยวกับ "การอ้างอิง" (เช่นint&) ที่ไม่เหมือนกับ Java และ C # 's "ประเภทการอ้างอิง" แต่ก็เหมือนกับ "การเรียกโดยการอ้างอิง" "ประเภทอ้างอิง" ของ Java และ C # และทุกประเภทใน Python เหมือนกับ C และ C ++ เรียกว่า "ประเภทตัวชี้" (เช่นint*)


ตกลงนี่คือคำอธิบายที่ยาวและเป็นทางการมากขึ้น

คำศัพท์

เริ่มต้นด้วยฉันต้องการเน้นบางส่วนของคำศัพท์ที่สำคัญเพื่อช่วยชี้แจงคำตอบของฉันและเพื่อให้แน่ใจว่าเราทุกคนอ้างถึงแนวคิดเดียวกันเมื่อเราใช้คำ (ในทางปฏิบัติฉันเชื่อว่าความสับสนส่วนใหญ่เกี่ยวกับหัวข้อต่าง ๆ เช่นเกิดจากการใช้คำในวิธีที่ไม่สื่อสารความหมายที่ตั้งใจไว้อย่างสมบูรณ์)

ในการเริ่มต้นนี่คือตัวอย่างในภาษา C บางอย่างของการประกาศฟังก์ชัน:

void foo(int param) {  // line 1
  param += 1;
}

และนี่คือตัวอย่างของการเรียกใช้ฟังก์ชันนี้:

void bar() {
  int arg = 1;  // line 2
  foo(arg);     // line 3
}

ใช้ตัวอย่างนี้ฉันต้องการกำหนดบิตที่สำคัญบางคำศัพท์:

  • fooเป็นฟังก์ชั่นที่ประกาศในบรรทัดที่ 1 (Java ยืนยันในการสร้างวิธีการฟังก์ชั่นทั้งหมด แต่แนวคิดจะเหมือนกันโดยไม่สูญเสียความนิยมทั่วไป C และ C ++ สร้างความแตกต่างระหว่างการประกาศและคำจำกัดความที่ฉันจะไม่เข้าไปที่นี่)
  • paramเป็นพารามิเตอร์อย่างเป็นทางการไปfooยังประกาศในบรรทัดที่ 1
  • argเป็นตัวแปรโดยเฉพาะอย่างยิ่งตัวแปรท้องถิ่นของฟังก์ชั่นbarประกาศและเริ่มต้นในบรรทัดที่ 2
  • argเป็นอาร์กิวเมนต์สำหรับการเรียกใช้เฉพาะของfooon line 3

มีแนวคิดที่สำคัญสองชุดที่แยกความแตกต่างได้ที่นี่ สิ่งแรกคือค่าเมื่อเทียบกับตัวแปร :

  • ค่าเป็นผลมาจากการประเมินการแสดงออกในภาษาที่ ยกตัวอย่างเช่นในbarฟังก์ชั่นข้างต้นหลังจากบรรทัดint arg = 1;, การแสดงออกargที่มีค่า 1
  • ตัวแปรเป็นภาชนะสำหรับค่า ตัวแปรสามารถเปลี่ยนแปลงได้ (นี่คือค่าเริ่มต้นในภาษา C-like ส่วนใหญ่) อ่านอย่างเดียว (เช่นประกาศโดยใช้ Java finalหรือ C # readonly) หรือเปลี่ยนแปลงไม่ได้อย่างลึกซึ้ง (เช่นใช้ C ++ const)

แนวคิดที่สำคัญอีกคู่ที่แยกความแตกต่างคือพารามิเตอร์เปรียบเทียบกับอาร์กิวเมนต์ :

  • พารามิเตอร์ (เรียกว่ายังเป็นพารามิเตอร์ที่เป็นทางการ ) เป็นตัวแปรที่จะต้องจัดทำโดยผู้ที่โทรมาเมื่อเรียกฟังก์ชั่น
  • อาร์กิวเมนต์เป็นค่าที่จัดทำโดยโทรของฟังก์ชั่นเพื่อตอบสนองอย่างเป็นทางการพารามิเตอร์ที่เฉพาะเจาะจงของฟังก์ชั่นที่

โทรตามค่า

ในการโทรตามค่าพารามิเตอร์ที่เป็นทางการของฟังก์ชันคือตัวแปรที่สร้างขึ้นใหม่สำหรับการเรียกใช้ฟังก์ชันและจะเริ่มต้นด้วยค่าของอาร์กิวเมนต์

สิ่งนี้ทำงานในลักษณะเดียวกับที่ตัวแปรชนิดอื่นใดจะเริ่มต้นด้วยค่า ตัวอย่างเช่น:

int arg = 1;
int another_variable = arg;

ที่นี่argและanother_variableเป็นตัวแปรอิสระอย่างสมบูรณ์ - ค่าของพวกเขาสามารถเปลี่ยนเป็นอิสระจากกัน อย่างไรก็ตามในจุดที่another_variableมีการประกาศก็จะเริ่มต้นที่จะเก็บค่าเดียวกันกับที่argถือ - 1ซึ่งเป็น

เนื่องจากเป็นตัวแปรอิสระการเปลี่ยนแปลงจึงanother_variableไม่ส่งผลกระทบarg:

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

ตรงนี้เป็นความสัมพันธ์ระหว่างargและparamในตัวอย่างของเราด้านบนซึ่งฉันจะทำซ้ำที่นี่เพื่อสมมาตร:

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

มันเหมือนกับว่าเราได้เขียนโค้ดด้วยวิธีนี้:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

นั่นคือลักษณะการกำหนดของสิ่งที่เรียกโดยค่าหมายถึงว่า callee ( fooในกรณีนี้) ได้รับค่าเป็นข้อโต้แย้ง แต่มีตัวแปรแยกต่างหากสำหรับค่าเหล่านั้นจากตัวแปรของผู้โทร ( barในกรณีนี้)

กลับไปที่คำอุปมาของฉันข้างต้นถ้าฉันbarและคุณfooเมื่อฉันโทรหาคุณฉันจะส่งกระดาษแผ่นหนึ่งให้คุณพร้อมค่าที่เขียน คุณเรียกกระดาษชิ้นparamนั้น ค่าที่เป็นสำเนาของค่าที่ผมได้เขียนไว้ในสมุดบันทึกของฉัน (ตัวแปรท้องถิ่นของฉัน) argในสายผมตัวแปร

(นอกเหนือไปจาก: ขึ้นอยู่กับฮาร์ดแวร์และระบบปฏิบัติการมีการเรียกการประชุมที่หลากหลายเกี่ยวกับวิธีที่คุณเรียกฟังก์ชั่นหนึ่งจากอีกการประชุมการโทรเป็นเหมือนเราตัดสินใจว่าฉันจะเขียนค่าลงบนกระดาษของฉันแล้วส่งให้คุณ หรือถ้าคุณมีกระดาษแผ่นหนึ่งที่ฉันเขียนไว้หรือถ้าฉันเขียนมันไว้บนกำแพงตรงหน้าเราทั้งคู่นี่เป็นเรื่องที่น่าสนใจเช่นกัน แต่ไกลเกินกว่าขอบเขตของคำตอบที่ยาวนานนี้)

โทรโดยอ้างอิง

ในการโทรโดยการอ้างอิงพารามิเตอร์อย่างเป็นทางการของฟังก์ชั่นเป็นเพียงชื่อใหม่สำหรับตัวแปรเดียวกับที่ผู้โทรเข้ามาเป็นข้อโต้แย้ง

กลับไปที่ตัวอย่างของเราด้านบนมันเทียบเท่ากับ:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

เนื่องจากparamเป็นเพียงอีกชื่อหนึ่งarg- นั่นคือพวกเขาเป็นตัวแปรเดียวกันการเปลี่ยนแปลงจะถูกแสดงในparam argนี่เป็นวิธีพื้นฐานที่การโทรโดยการอ้างอิงแตกต่างจากการโทรตามค่า

มีภาษาน้อยมากที่สนับสนุนการอ้างอิง แต่ C ++ สามารถทำสิ่งนี้ได้:

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

ในกรณีparamนี้ไม่เพียง แต่มีค่าเดียวกับที่argจริงมันคือ arg (โดยชื่ออื่น) และbarสามารถสังเกตargได้ว่ามันเพิ่มขึ้น

โปรดทราบว่านี่ไม่ใช่วิธีการของ Java, JavaScript, C, Objective-C, Python หรือภาษายอดนิยมอื่น ๆ ในปัจจุบัน ซึ่งหมายความว่าภาษาเหล่านั้นไม่ได้ถูกเรียกโดยการอ้างอิงพวกเขาจะโทรตามค่า

ภาคผนวก: โทรตามการแชร์วัตถุ

หากสิ่งที่คุณมีคือการเรียกตามค่าแต่ค่าจริงเป็นประเภทการอ้างอิงหรือประเภทตัวชี้แล้ว "ค่า" ตัวเองไม่น่าสนใจมาก (เช่นใน C เป็นเพียงจำนวนเต็มขนาดเฉพาะแพลตฟอร์ม) - สิ่งที่ ที่น่าสนใจคืออะไรที่คุ้มค่าชี้ไปที่

หากประเภทการอ้างอิง (นั่นคือตัวชี้) ชี้ไปที่ไม่แน่นอนแล้วผลที่น่าสนใจเป็นไปได้: คุณสามารถปรับเปลี่ยนค่าชี้ไปที่และผู้โทรสามารถสังเกตเห็นการเปลี่ยนแปลงค่าชี้ไปที่แม้ว่าผู้โทรไม่สามารถสังเกตเห็น เปลี่ยนเป็นตัวชี้

ในการขอยืม URL ที่คล้ายคลึงกันอีกครั้งข้อเท็จจริงที่ว่าฉันให้คุณคัดลอก URL ไปยังเว็บไซต์นั้นไม่น่าสนใจเป็นพิเศษหากสิ่งที่เราทั้งคู่ใส่ใจคือเว็บไซต์ไม่ใช่ URL ความจริงที่ว่าคุณเขียนหวัดคัดลอก URL ของคุณไม่มีผลกับสำเนา URL ของฉันไม่ใช่สิ่งที่เราใส่ใจ (และอันที่จริงแล้วในภาษาเช่น Java และ Python นั้น "URL" หรือค่าประเภทอ้างอิงสามารถ ไม่สามารถแก้ไขได้เลยสิ่งเดียวเท่านั้นที่ชี้ได้)

Barbara Liskov เมื่อเธอคิดค้นภาษาการเขียนโปรแกรม CLU (ซึ่งมีความหมายเหล่านี้) ได้ตระหนักว่าคำที่มีอยู่ "การเรียกตามค่า" และ "การโทรโดยการอ้างอิง" ไม่มีประโยชน์อย่างยิ่งสำหรับการอธิบายความหมายของภาษาใหม่นี้ ดังนั้นเธอจึงคิดค้นคำใหม่: โทรโดยใช้งานร่วมกันของวัตถุ

เมื่อพูดถึงภาษาที่มีการเรียกตามค่าทางเทคนิค แต่ที่ประเภทการใช้งานทั่วไปคือการอ้างอิงหรือประเภทตัวชี้ (นั่นคือเกือบทุกความจำเป็นที่ทันสมัยภาษาการเขียนโปรแกรมเชิงวัตถุหรือหลายกระบวนทัศน์) ฉันพบว่ามันสับสนน้อยกว่า เพียงแค่หลีกเลี่ยงการพูดคุยเกี่ยวกับการเรียกร้องโดยค่าหรือโทรโดยการอ้างอิง ติดเพื่อโทรด้วยการแบ่งปันวัตถุ (หรือเพียงแค่โทรตามวัตถุ ) และจะไม่มีใครสับสน :-)


อธิบายได้ดีขึ้น: มีแนวคิดที่สำคัญสองชุดที่แยกความแตกต่างได้ที่นี่ The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat

2
คำตอบที่ยอดเยี่ยม ฉันคิดว่าฉันจะเพิ่มที่ไม่จำเป็นต้องสร้างที่เก็บข้อมูลใหม่โดยการอ้างอิง ชื่อพารามิเตอร์อ้างอิงที่เก็บข้อมูลดั้งเดิม (หน่วยความจำ) ขอบคุณ
drlolly

1
คำตอบที่ดีที่สุด IMO
Rafael Eyng

59

ก่อนที่จะเข้าใจคำศัพท์ 2 ข้อคุณต้องเข้าใจสิ่งต่อไปนี้ วัตถุทุกชิ้นมี 2 สิ่งที่สามารถทำให้แตกต่าง

  • คุณค่าของมัน
  • ที่อยู่ของมัน

ดังนั้นถ้าคุณพูด employee.name = "John"

รู้ว่ามี 2 nameสิ่งที่เกี่ยวกับ ค่าของมันซึ่งเป็นและยังเป็นที่ตั้งของมันในหน่วยความจำซึ่งเป็นเลขฐานสิบหกบางอาจจะเช่นนี้"John"0x7fd5d258dd00

ขึ้นอยู่กับสถาปัตยกรรมของภาษาหรือชนิด (คลาส, โครงสร้าง, ฯลฯ ) ของวัตถุของคุณคุณอาจกำลังถ่ายโอน"John"หรือ0x7fd5d258dd00

การผ่าน"John"เป็นที่รู้จักกันว่าผ่านค่า การผ่าน0x7fd5d258dd00เป็นที่รู้จักกันว่าผ่านโดยการอ้างอิง "John"ทุกคนที่จะชี้ไปที่สถานที่ตั้งของหน่วยความจำนี้จะมีการเข้าถึงค่าของ

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ฉันขอแนะนำให้คุณอ่านเกี่ยวกับการยกเลิกการอ้างอิงตัวชี้และทำไมต้องเลือก struct (ประเภทค่า) บนคลาส (ประเภทอ้างอิง)


3
นั่นคือฉันกำลังมองหาจริง ๆ แล้วคนหนึ่งควรมองหาแนวคิดที่ไม่เพียง แต่คำอธิบายยกนิ้วให้
Haisum Usman

Java ผ่านค่าเสมอ การส่งผ่านการอ้างอิงไปยังวัตถุใน java ถือเป็นการส่งผ่านตามค่า สิ่งนี้ขัดแย้งกับคำสั่งของคุณ "Passing 0x7fd5d258dd00 หรือที่เรียกว่าการส่งต่อโดยการอ้างอิง"
chetan raina

53

นี่คือตัวอย่าง:

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}

1
ฉันคิดว่ามีปัญหาอย่างหนึ่งที่บรรทัดสุดท้ายควรพิมพ์ 0 แทนที่จะเป็น 2 โปรดบอกฉันว่าฉันทำอะไรหาย
Taimoor Changaiz

@TaimoorChangaiz; "บรรทัดสุดท้าย" ใด ถ้าคุณสามารถใช้ IRC ได้โปรดมาที่ ## programming บน Freenode มันจะง่ายกว่าที่จะอธิบายสิ่งต่าง ๆ ที่นั่น นิคของฉันมี "pyon"
pyon

1
@ EduardoLeón by_val (y); std :: ศาล << y << std :: endl; // ภาพพิมพ์ 2
Taimoor Changaiz

5
@TaimoorChangaiz: ทำไมถึงไม่พิมพ์ 2 yถูกตั้งค่าเป็น 2 โดยบรรทัดก่อนหน้าแล้ว ทำไมมันกลับเป็น 0
pyon

@ EduardoLeónฉันไม่ดี ใช่คุณถูก. ขอบคุณสำหรับการแก้ไข
Taimoor Changaiz

28

วิธีที่ง่ายที่สุดในการรับไฟล์นี้คือไฟล์ Excel สมมุติว่าคุณมีตัวเลขสองตัวคือ 5 และ 2 ในเซลล์ A1 และ B1 และคุณต้องการหาผลรวมของพวกมันในเซลล์ที่สามสมมุติว่า A2 คุณสามารถทำได้สองวิธี

  • โดยการส่งค่าไปยังเซลล์ A2โดยพิมพ์= 5 + 2ลงในเซลล์นี้ ในกรณีนี้หากค่าของเซลล์ A1 หรือ B1 เปลี่ยนไปผลรวมใน A2 จะยังคงเหมือนเดิม

  • หรือโดยการผ่าน“อ้างอิง” ของเซลล์ A1 และ B1 เซลล์ A2โดยพิมพ์= A1 + B1 ในกรณีนี้หากค่าของเซลล์ A1 หรือ B1 เปลี่ยนไปผลรวมใน A2 ก็จะเปลี่ยนเช่นกัน


นี่คือตัวอย่างที่ง่ายที่สุดและดีที่สุดในบรรดาคำตอบอื่น ๆ
Amit Ray

18

เมื่อผ่านโดยอ้างอิงคุณจะผ่านตัวชี้ไปที่ตัวแปร ผ่านค่าที่คุณกำลังผ่านสำเนาของตัวแปร ในการใช้งานขั้นพื้นฐานนี้หมายถึงการเปลี่ยนผ่านโดยอ้างอิงกับตัวแปรที่จะเห็นเป็นวิธีการโทรและผ่านค่าที่พวกเขาเคยชิน


12

ผ่านค่าส่งสำเนาของข้อมูลที่เก็บไว้ในตัวแปรที่คุณระบุผ่านการอ้างอิงส่งลิงค์โดยตรงไปยังตัวแปรเอง ดังนั้นถ้าคุณส่งตัวแปรโดยการอ้างอิงแล้วเปลี่ยนตัวแปรภายในบล็อกที่คุณส่งเข้าไปตัวแปรเดิมจะถูกเปลี่ยน หากคุณผ่านค่าตัวแปรเดิมจะไม่สามารถเปลี่ยนได้โดยบล็อกที่คุณส่งเข้าไป แต่คุณจะได้รับสำเนาของสิ่งที่มีอยู่ในช่วงเวลาของการโทร


7

ผ่านค่า - ฟังก์ชั่นคัดลอกตัวแปรและทำงานกับสำเนา (ดังนั้นจึงไม่เปลี่ยนแปลงอะไรในตัวแปรดั้งเดิม)

ผ่านการอ้างอิง - ฟังก์ชั่นใช้ตัวแปรเดิมถ้าคุณเปลี่ยนตัวแปรในฟังก์ชั่นอื่น ๆ ก็จะเปลี่ยนตัวแปรเดิมเช่นกัน

ตัวอย่าง (คัดลอกและใช้ / ลองด้วยตัวคุณเองและดู):

#include <iostream>

using namespace std;

void funct1(int a){ //pass-by-value
    a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
    a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
    funct2(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7

    return 0;
}

ทำให้ง่าย peeps กำแพงข้อความอาจเป็นนิสัยที่ไม่ดี


สิ่งนี้มีประโยชน์มากในการทำความเข้าใจว่าค่าพารามิเตอร์มีการเปลี่ยนแปลงหรือไม่ขอบคุณ!
Kevin Zhao

5

ความแตกต่างที่สำคัญระหว่างพวกเขาคือตัวแปรประเภทค่าเก็บค่าดังนั้นการระบุตัวแปรประเภทค่าในการเรียกวิธีการส่งสำเนาของค่าของตัวแปรนั้นไปยังวิธีการ ตัวแปรประเภทการอ้างอิงเก็บการอ้างอิงไปยังวัตถุดังนั้นการระบุตัวแปรประเภทการอ้างอิงเป็นอาร์กิวเมนต์ผ่านวิธีการคัดลอกของการอ้างอิงจริงที่อ้างอิงถึงวัตถุ แม้ว่าการอ้างอิงจะถูกส่งผ่านตามค่าวิธีการนั้นยังสามารถใช้การอ้างอิงที่ได้รับเพื่อโต้ตอบกับ - และอาจแก้ไขได้ - วัตถุดั้งเดิม ในทำนองเดียวกันเมื่อส่งคืนข้อมูลจากวิธีการผ่านคำสั่งส่งคืนวิธีการส่งคืนสำเนาของค่าที่เก็บไว้ในตัวแปรประเภทค่าหรือสำเนาของการอ้างอิงที่เก็บไว้ในตัวแปรประเภทการอ้างอิง เมื่อมีการส่งคืนการอ้างอิงวิธีการโทรสามารถใช้การอ้างอิงนั้นเพื่อโต้ตอบกับวัตถุที่ถูกอ้างอิง ดังนั้น,

ใน c # การส่งผ่านตัวแปรโดยการอ้างอิงเพื่อให้วิธีการที่เรียกว่าสามารถแก้ไขตัวแปร C # ให้คำหลักอ้างอิงและออก การใช้คำหลักอ้างอิงกับการประกาศพารามิเตอร์ช่วยให้คุณสามารถส่งตัวแปรไปยังวิธีการโดยอ้างอิง - วิธีการที่เรียกว่าจะสามารถแก้ไขตัวแปรเดิมในการโทร คีย์เวิร์ด ref ใช้สำหรับตัวแปรที่ถูกกำหนดค่าเริ่มต้นในวิธีการโทรแล้ว โดยปกติเมื่อการเรียกเมธอดมีตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นเป็นอาร์กิวเมนต์คอมไพเลอร์จะสร้างข้อผิดพลาด การนำหน้าพารามิเตอร์ด้วยคีย์เวิร์ด out สร้างพารามิเตอร์เอาต์พุต สิ่งนี้บ่งชี้ให้คอมไพเลอร์ว่าอาร์กิวเมนต์จะถูกส่งผ่านไปยังวิธีการที่เรียกว่าโดยการอ้างอิงและวิธีการที่เรียกว่าจะกำหนดค่าให้กับตัวแปรเดิมในการโทร หากวิธีนี้ไม่ได้กำหนดค่าให้กับพารามิเตอร์เอาต์พุตในทุกเส้นทางที่เป็นไปได้ของการดำเนินการคอมไพเลอร์จะสร้างข้อผิดพลาด สิ่งนี้ยังป้องกันไม่ให้คอมไพเลอร์สร้างข้อความแสดงข้อผิดพลาดสำหรับตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นที่ถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังเมธอด เมธอดสามารถส่งคืนค่าเดียวให้แก่ผู้เรียกผ่านทางคำสั่ง return แต่สามารถส่งคืนค่าจำนวนมากได้โดยระบุพารามิเตอร์เอาต์พุตหลายตัว (ref และ / หรือ out)

ดูการสนทนา c # และตัวอย่างที่นี่ลิงค์ข้อความ


3

ตัวอย่าง:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const &โดยทั่วไปจะดีที่สุด คุณไม่ต้องเสียค่าก่อสร้างและค่าปรับจากการทำลายล้าง หากการอ้างอิงไม่เชื่อมต่ออินเทอร์เฟซของคุณกำลังแนะนำว่ามันจะเปลี่ยนการส่งผ่านข้อมูล


2

ในระยะสั้นผ่านตามมูลค่าคือสิ่งที่มันเป็นและผ่านการอ้างอิงคือที่มันเป็น

หากค่าของคุณคือ VAR1 = "Happy Guy!" คุณจะเห็น "Happy Guy!" เท่านั้น หาก VAR1 เปลี่ยนเป็น "Happy Gal!" คุณจะไม่รู้ตัว ถ้ามันผ่านการอ้างอิงและการเปลี่ยนแปลง VAR1 คุณจะ


2

หากคุณไม่ต้องการเปลี่ยนค่าของตัวแปรดั้งเดิมหลังจากส่งผ่านไปยังฟังก์ชันฟังก์ชันควรสร้างด้วยพารามิเตอร์" pass by value "

จากนั้นฟังก์ชันจะมีเฉพาะค่า แต่ไม่ใช่ที่อยู่ของตัวแปรที่ส่งผ่าน หากไม่มีที่อยู่ของตัวแปรรหัสภายในฟังก์ชั่นจะไม่สามารถเปลี่ยนค่าตัวแปรตามที่เห็นจากด้านนอกของฟังก์ชัน

แต่ถ้าคุณต้องการที่จะให้ฟังก์ชั่นความสามารถในการเปลี่ยนค่าของตัวแปรเท่าที่เห็นจากภายนอกที่คุณจำเป็นต้องใช้ผ่านการอ้างอิง เนื่องจากทั้งค่าและที่อยู่ (การอ้างอิง) ถูกส่งผ่านและพร้อมใช้งานภายในฟังก์ชัน


1

pass by value หมายถึงวิธีส่งค่าไปยังฟังก์ชันโดยใช้อาร์กิวเมนต์ ในการผ่านค่าเราคัดลอกข้อมูลที่เก็บไว้ในตัวแปรที่เราระบุและมันช้ากว่าผ่านโดยอ้างอิง bcse t เขาข้อมูลจะถูกคัดลอก ของเราทำการเปลี่ยนแปลงข้อมูลที่คัดลอกข้อมูลต้นฉบับจะไม่ได้รับผลกระทบ ในการส่งผ่านโดยการอ้างอิงหรือผ่านที่อยู่เราจะส่งลิงค์โดยตรงไปยังตัวแปรเอง หรือผ่านตัวชี้ไปยังตัวแปร มันเร็วขึ้น bcse ใช้เวลาน้อยลง


0

นี่คือตัวอย่างที่แสดงให้เห็นถึงความแตกต่างระหว่างการส่งผ่านค่า - ตัวชี้ - การอ้างอิง :

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

“การผ่านโดยการอ้างอิง” วิธีการมีข้อ จำกัด ที่สำคัญ ถ้าพารามิเตอร์ถูกประกาศเป็นผ่านอ้างอิง (จึงนำหน้าด้วยและเข้าสู่ระบบ) มันสอดคล้องพารามิเตอร์ที่เกิดขึ้นจริงจะต้องเป็นตัวแปร

พารามิเตอร์จริงที่อ้างอิงถึงพารามิเตอร์ทางการของ "การส่งผ่านตามค่า" อาจเป็นนิพจน์โดยทั่วไปดังนั้นจึงอนุญาตให้ใช้ไม่เพียง แต่ตัวแปรเท่านั้น แต่ยังเป็นผลลัพธ์ที่แท้จริง

ฟังก์ชันไม่สามารถวางค่าในสิ่งอื่นที่ไม่ใช่ตัวแปร ไม่สามารถกำหนดค่าใหม่ให้กับตัวอักษรหรือบังคับให้นิพจน์เปลี่ยนผลลัพธ์ได้

PS: คุณสามารถตรวจสอบคำตอบ Dylan Beattie ในหัวข้อปัจจุบันที่อธิบายด้วยคำพูดธรรมดา


คุณระบุ "ถ้าพารามิเตอร์ถูกประกาศ [เป็นการอ้างอิง] พารามิเตอร์จริงที่สอดคล้องกันต้องเป็นตัวแปร" แต่นั่นไม่เป็นความจริงโดยทั่วไป หากการอ้างอิงถูกผูกไว้กับชั่วคราว (เช่นค่าส่งคืนของฟังก์ชั่น) อายุการใช้งานจะถูกขยายให้ตรงกับการอ้างอิง ดูรายละเอียดที่นี่
Chris Hunt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.