ควรใช้การอ้างอิงกับพอยน์เตอร์เมื่อใด


381

ฉันเข้าใจไวยากรณ์และความหมายทั่วไปของพอยน์เตอร์กับการอ้างอิง แต่ฉันจะตัดสินใจได้อย่างไรว่าควรจะใช้การอ้างอิงหรือพอยน์เตอร์ใน API เหมาะสมกว่าหรือน้อยกว่านั้น

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

เช่นในรหัสต่อไปนี้:

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

ด้วยตัวชี้จะเห็นได้ชัดว่ามีอะไรเกิดขึ้นอยู่เสมอดังนั้นสำหรับ API และสิ่งที่ความชัดเจนเป็นข้อกังวลที่สำคัญคือตัวชี้ไม่เหมาะสมกว่าการอ้างอิงหรือไม่ หมายความว่าควรใช้การอ้างอิงเมื่อจำเป็นเท่านั้น (เช่นoperator++)? มีข้อกังวลเกี่ยวกับประสิทธิภาพหรือไม่

แก้ไข (ล้าสมัย):

นอกเหนือจากการอนุญาตให้มีค่า NULL และจัดการกับอาร์เรย์แบบดิบแล้วดูเหมือนว่าตัวเลือกจะลงมาตามความชอบส่วนตัว ฉันยอมรับคำตอบด้านล่างที่อ้างอิงถึงคู่มือสไตล์ C ++ ของ Googleเนื่องจากพวกเขานำเสนอมุมมองที่ว่า "การอ้างอิงอาจทำให้เกิดความสับสนเนื่องจากมีไวยากรณ์ของค่า แต่มีความหมายของตัวชี้"

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


4
ดูเหมือนว่าคุณได้ตัดสินใจแล้วว่าจะใช้อันไหนเมื่อไหร่ โดยส่วนตัวแล้วฉันชอบที่จะส่งต่อวัตถุที่ฉันกำลังทำอยู่ไม่ว่าจะดัดแปลงหรือไม่ก็ตาม ถ้าฟังก์ชั่นใช้พอยน์เตอร์มันจะบอกว่ามันทำหน้าที่พอยน์เตอร์นั่นคือใช้มันเป็นตัววนซ้ำในอาเรย์
Benjamin Lindley

1
@Schnommus: ยุติธรรมมากฉันใช้ TextMate เป็นส่วนใหญ่ ถึงกระนั้นฉันคิดว่ามันจะดีกว่าที่ความหมายจะชัดเจนจากอย่างรวดเร็ว
connec

4
สิ่งที่เกี่ยวกับadd_one(a);เป็นที่ชัดเจนว่าa's จะได้รับการแก้ไข? มันบอกว่าสิทธิในรหัส: เพิ่มหนึ่ง
GManNickG

32
@connec: คู่มือสไตล์ Google C ++ ไม่ถือว่าเป็นแนวทางสไตล์ C ++ ที่ดี มันเป็นคู่มือสไตล์สำหรับการทำงานกับฐานรหัส C ++ เก่าของ Google (เหมาะสำหรับสิ่งของของพวกเขา) ยอมรับคำตอบจากสิ่งที่ไม่ได้ช่วยใคร เพียงแค่อ่านความคิดเห็นและคำอธิบายของคุณคุณมาที่คำถามนี้ด้วยความเห็นที่ตั้งไว้แล้วและกำลังมองหาคนอื่นเพื่อยืนยันมุมมองของคุณ ดังนั้นคุณจะต้องตั้งคำถามและตอบคำถามที่คุณต้องการได้ยิน
Martin York

1
addOneTo(...)นี้ได้รับการแก้ไขโดยการตั้งชื่อเพียงวิธีการที่ ถ้านั่นไม่ใช่สิ่งที่คุณต้องการทำเพียงแค่ดูที่การประกาศ
ฟาน

คำตอบ:


296

ใช้การอ้างอิงทุกที่ที่คุณสามารถพอยน์เตอร์ได้ทุกที่

หลีกเลี่ยงพอยน์เตอร์จนกว่าคุณจะทำไม่ได้

เหตุผลก็คือพอยน์เตอร์ทำให้สิ่งที่ยากต่อการติดตาม / อ่านปลอดภัยน้อยกว่าและการจัดการที่อันตรายยิ่งกว่าสิ่งก่อสร้างอื่น ๆ

ดังนั้นกฎของหัวแม่มือคือการใช้ตัวชี้เฉพาะในกรณีที่ไม่มีทางเลือกอื่น

ตัวอย่างเช่นการส่งคืนพอยน์เตอร์ไปยังวัตถุเป็นตัวเลือกที่ถูกต้องเมื่อฟังก์ชั่นสามารถคืนค่า nullptr ในบางกรณีและมันจะถือว่ามันเป็น boost::optionalที่กล่าวว่าเป็นตัวเลือกที่ดีกว่าที่จะใช้สิ่งที่คล้ายกับ

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

ในตัวอย่างของคุณไม่มีจุดในการใช้ตัวชี้เป็นอาร์กิวเมนต์เนื่องจาก:

  1. หากคุณให้nullptrเป็นอาร์กิวเมนต์คุณจะไปใน undefined-behavior-land
  2. เวอร์ชันแอตทริบิวต์การอ้างอิงไม่อนุญาตให้ (โดยไม่มีการเล็งที่ง่าย) ปัญหาเกี่ยวกับ 1
  3. เวอร์ชันแอตทริบิวต์การอ้างอิงนั้นเข้าใจง่ายสำหรับผู้ใช้: คุณต้องระบุวัตถุที่ถูกต้องไม่ใช่สิ่งที่อาจเป็นโมฆะ

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


49
ฉันไม่แน่ใจว่าพอยน์เตอร์ทำอะไรได้ยากกว่าในการอ่าน? มันเป็นแนวคิดที่ค่อนข้างเรียบง่ายและทำให้ชัดเจนเมื่อมีบางสิ่งที่น่าจะแก้ไข หากมีสิ่งใดที่ฉันบอกว่าเป็นการยากที่จะอ่านเมื่อไม่มีสิ่งบ่งชี้ว่าเกิดอะไรขึ้นทำไมadd_one(a)ไม่ควรส่งคืนผลลัพธ์แทนที่จะตั้งค่าโดยอ้างอิง
เชื่อมต่อ

46
@connec: หากadd_one(a)เกิดความสับสนนั่นเป็นเพราะมีการตั้งชื่อไม่ถูกต้อง add_one(&a)จะมีความสับสนเหมือนกันเพียงตอนนี้คุณอาจจะเพิ่มตัวชี้และไม่ใช่วัตถุ add_one_inplace(a)จะหลีกเลี่ยงความสับสนทั้งหมด
Nicol Bolas

20
จนถึงจุดหนึ่งการอ้างอิงสามารถอ้างถึงหน่วยความจำที่สามารถหายไปได้อย่างง่ายดายเช่นเดียวกับตัวชี้สามารถ ดังนั้นพวกเขาจึงไม่จำเป็นต้องปลอดภัยกว่าพอยน์เตอร์ใด ๆ การอ้างอิงอย่างต่อเนื่องและการส่งผ่านอาจเป็นอันตรายเท่ากับตัวชี้
Doug T.

6
@ การอ้างสิทธิ์ฉันหมายถึงตัวชี้แบบดิบ ฉันหมายถึงว่า C ++ มีพอยน์เตอร์NULLและnullptrและมันมีเหตุผล และไม่ใช่คำแนะนำที่ได้รับการพิจารณาอย่างดีหรือเป็นจริงเพื่อให้ "ไม่ใช้พอยน์เตอร์" และ / หรือ "ไม่เคยใช้ NULL ใช้เสมอboost::optional" นั่นเป็นบ้า อย่าเข้าใจฉันผิดพอยน์เตอร์ดิบจำเป็นต้องใช้น้อยกว่าใน C ++ มากกว่าใน C แต่ก็ยังมีประโยชน์พวกเขาไม่ได้ "อันตราย" เหมือนคน C ++ บางคนชอบที่จะเรียกร้อง (นั่นเป็นการพูดเกินจริง) และอีกครั้ง: เมื่อใช้ตัวชี้ได้ง่ายขึ้นและreturn nullptr;ระบุค่าที่ขาดหายไป ... ทำไมจึงต้องนำเข้า Boost ทั้งหมด

5
@ การประกาศว่า "การใช้ NULL เป็นการปฏิบัติที่ไม่ดี" - ตอนนี้มันไร้สาระ และifล้าสมัยและควรใช้while() { break; }แทนใช่ไหม นอกจากนี้ไม่ต้องกังวลฉันได้เห็นและทำงานกับฐานรหัสขนาดใหญ่และใช่ถ้าคุณประมาทการเป็นเจ้าของก็เป็นปัญหา ไม่ใช่ถ้าคุณยึดมั่นในอนุสัญญาให้ใช้อย่างสม่ำเสมอและแสดงความคิดเห็นและจัดทำเอกสารรหัสของคุณ แต่ท้ายที่สุดฉันควรใช้ C เพราะฉันโง่เกินไปสำหรับ C ++ ใช่ไหม

62

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

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

ระเบียบการเข้ารหัสบางอย่าง (เช่นของ Google ) กำหนดว่าควรใช้พอยน์เตอร์หรือการอ้างอิง const เสมอเนื่องจากการอ้างอิงมีไวยากรณ์ที่ไม่ชัดเจน: พวกเขามีพฤติกรรมการอ้างอิง แต่ไวยากรณ์ของค่า


10
เพียงเพิ่มนิด ๆ หน่อย ๆคู่มือสไตล์ของ Google บอกว่าพารามิเตอร์อินพุตสำหรับฟังก์ชั่นควรเป็น const การอ้างอิงและผลลัพธ์ควรเป็นตัวชี้ ฉันชอบสิ่งนี้เพราะมันชัดเจนมากเมื่อคุณอ่านลายเซ็นของฟังก์ชั่นคืออะไรอินพุตและเอาต์พุตคืออะไร
ด่าน

44
@Dan: คู่มือสไตล์ของ Google ใช้สำหรับรหัสเก่าของ Google และไม่ควรใช้สำหรับการเข้ารหัสที่ทันสมัย ในความเป็นจริงมันเป็นรูปแบบการเขียนโปรแกรมที่ไม่ดีสำหรับโครงการใหม่
GManNickG

13
@connec: ให้ฉันใส่มันด้วยวิธีนี้: null เป็นค่าตัวชี้ที่ถูกต้องสมบูรณ์ ทุกที่ที่มีตัวชี้ฉันสามารถให้ค่าเป็นศูนย์ได้ Ergo รุ่นที่สองของคุณadd_oneจะเสีย : add_one(0); // passing a perfectly valid pointer value, Kaboom คุณต้องตรวจสอบว่ามันว่างหรือไม่ บางคนจะโต้กลับ: "ฉันจะทำเอกสารว่าฟังก์ชั่นของฉันใช้ไม่ได้กับโมฆะ" ที่ดี แต่แล้วคุณพ่ายแพ้วัตถุประสงค์ของคำถาม: ถ้าคุณกำลังจะไปดูเอกสารประกอบเพื่อดูว่า null ไม่เป็นไรคุณจะเห็นการประกาศฟังก์ชัน
GManNickG

8
หากเป็นการอ้างอิงคุณจะเห็นว่าเป็นกรณี การโต้กลับดังกล่าวไม่ถึงจุด: การอ้างอิงบังคับใช้ในระดับภาษาที่อ้างถึงวัตถุที่มีอยู่และอาจไม่เป็นโมฆะในขณะที่พอยน์เตอร์ไม่มีข้อ จำกัด ดังกล่าว ฉันคิดว่าเป็นที่ชัดเจนว่าการบังคับใช้ระดับภาษานั้นมีประสิทธิภาพมากกว่าและมีข้อผิดพลาดน้อยกว่าการบังคับใช้ระดับเอกสาร บางคนจะพยายามที่จะฆ่าเชื้อนี้โดยกล่าวว่า "ดูโมฆะอ้างอิง: int& i = *((int*)0);นี้ไม่ได้เป็นโต้ที่ถูกต้อง.. ปัญหาในรหัสโกหกก่อนหน้านี้ที่มีการใช้ตัวชี้ไม่ได้กับการอ้างอิงอ้างอิงจะไม่ null ระยะเวลา..
GManNickG

12
สวัสดีฉันเห็นนักกฎหมายด้านภาษาขาดความคิดเห็นดังนั้นให้ฉันแก้ไข: การอ้างอิงมักจะถูกนำมาใช้โดยพอยน์เตอร์ แต่มาตรฐานบอกว่าไม่มีสิ่งนั้น การดำเนินการโดยใช้กลไกอื่นจะเป็นเรื่องร้องเรียน 100%
Thomas Bonini

34

จากC ++ FAQ Lite -

ใช้การอ้างอิงเมื่อคุณสามารถและตัวชี้เมื่อคุณต้อง

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

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

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


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

1
@connec: แน่ใจว่าโปรแกรมเมอร์ C นั้นถูกต้องสำหรับภาษาของพวกเขา แต่อย่าทำผิดในการรักษา C ++ เป็น C มันเป็นภาษาที่แตกต่างอย่างสิ้นเชิง หากคุณปฏิบัติต่อ C ++ ในฐานะ C คุณจะต้องเขียนสิ่งที่ผู้อ้างอิงอ้างว่าเป็นC with class(ซึ่งไม่ใช่ C ++)
Martin York

15

กฎง่ายๆของฉันคือ:

  • ใช้พอยน์เตอร์สำหรับพารามิเตอร์ขาออกหรือเข้า / ออก ดังนั้นจะเห็นได้ว่าค่ากำลังจะเปลี่ยน (คุณต้องใช้&)
  • ใช้ตัวชี้หากพารามิเตอร์ NULL เป็นค่าที่ยอมรับได้ (ตรวจสอบให้แน่ใจว่ามันconstเป็นพารามิเตอร์ที่เข้ามา)
  • ใช้การอ้างอิงสำหรับพารามิเตอร์ขาเข้าหากไม่สามารถเป็น NULL และไม่ใช่ประเภทพื้นฐาน ( const T&)
  • ใช้พอยน์เตอร์หรือพอยน์เตอร์อัจฉริยะเมื่อส่งคืนวัตถุที่สร้างขึ้นใหม่
  • ใช้พอยน์เตอร์หรือพอยน์เตอร์อัจฉริยะเป็นสมาชิก struct หรือ class แทนการอ้างอิง
  • ใช้การอ้างอิงสำหรับนามแฝง (เช่นint &current = someArray[i])

ไม่ว่าคุณจะใช้อันไหนอย่าลืมจัดทำเอกสารฟังก์ชั่นและความหมายของพารามิเตอร์ถ้าไม่ชัดเจน


14

คำเตือน: นอกเหนือจากความจริงที่ว่าการอ้างอิงไม่สามารถเป็นค่า NULL หรือ "เด้ง" (หมายความว่าไม่สามารถเปลี่ยนวัตถุที่พวกเขาเป็นนามแฝง) มันลงมาเป็นเรื่องของรสนิยมดังนั้นฉันจะไม่พูด "นี่ดีกว่า"

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

add_one(&a);

อาจชัดเจนกว่า

add_one(a);

เนื่องจากคุณรู้ว่าค่าของ a ส่วนใหญ่จะเปลี่ยนไป ในขณะที่ลายเซ็นของฟังก์ชั่น

void add_one(int* const n);

ค่อนข้างชัดเจนเช่นกัน: n จะเป็นจำนวนเต็มเดียวหรืออาร์เรย์หรือไม่ บางครั้งคุณสามารถเข้าถึงส่วนหัว (เอกสารไม่ดี) และลายเซ็นเช่น

foo(int* const a, int b);

ไม่ใช่เรื่องง่ายที่จะตีความตั้งแต่แรกเห็น

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


ขอบคุณสำหรับการสาธิตที่ชัดเจนว่าทั้งสองวิธีได้รับและสูญเสียความชัดเจน ตอนแรกฉันอยู่ในค่ายชี้ แต่สิ่งนี้สมเหตุสมผลมาก
Zach Beavon-Collin

12

เช่นเดียวกับที่อื่น ๆ ตอบแล้ว: ใช้การอ้างอิงเสมอยกเว้นตัวแปรที่เป็นNULL/ nullptrเป็นสถานะที่ถูกต้องจริงๆ

มุมมองของ John Carmack ในเรื่องนั้นคล้ายกัน:

ตัวชี้ NULL เป็นปัญหาที่ใหญ่ที่สุดใน C / C ++ อย่างน้อยในรหัสของเรา การใช้สองค่าเดียวเนื่องจากทั้งค่าสถานะและที่อยู่ทำให้เกิดปัญหาร้ายแรงจำนวนมาก การอ้างอิง C ++ ควรได้รับการสนับสนุนมากกว่าพอยน์เตอร์เมื่อทำได้ ในขณะที่การอ้างอิงคือ“ จริงๆ” เพียงแค่ตัวชี้ แต่ก็มีสัญญาโดยนัยว่าไม่ใช่ -NULL ทำการตรวจสอบ NULL เมื่อตัวชี้จะกลายเป็นการอ้างอิงจากนั้นคุณสามารถละเว้นปัญหาหลังจากนั้น

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

แก้ไข 2012-03-13

ผู้ใช้Bret Kuhnsพูดอย่างถูกต้อง:

มาตรฐาน C ++ 11 ได้รับการสรุป ฉันคิดว่าถึงเวลาแล้วในกระทู้นี้ที่จะกล่าวถึงว่าโค้ดส่วนใหญ่ควรจะทำงานได้อย่างสมบูรณ์แบบด้วยการผสมผสานของการอ้างอิง, shared_ptr และ unique_ptr

จริงพอ แต่คำถามยังคงอยู่แม้ว่าจะแทนที่ตัวชี้แบบดิบด้วยตัวชี้แบบสมาร์ทก็ตาม

ตัวอย่างเช่นทั้งstd::unique_ptrและstd::shared_ptrสามารถสร้างเป็นตัวชี้ "ว่าง" ผ่านตัวสร้างเริ่มต้นของพวกเขา:

... หมายความว่าการใช้งานโดยไม่ต้องตรวจสอบว่าไม่มีความเสี่ยงต่อการขัดข้องซึ่งเป็นสิ่งที่การอภิปรายของ J. Carmack เป็นเรื่องเกี่ยวกับ

จากนั้นเรามีปัญหาที่น่าขบขันของ "เราจะส่งตัวชี้สมาร์ทเป็นพารามิเตอร์ฟังก์ชันได้อย่างไร"

คำตอบของJonสำหรับคำถามC ++ - การส่งผ่านการอ้างอิงเพื่อเพิ่ม :: shared_ptrและความคิดเห็นต่อไปนี้แสดงให้เห็นว่าถึงแม้ว่าการส่งสมาร์ทพ้อยท์ด้วยการคัดลอกหรือการอ้างอิงยังไม่ชัดเจนเหมือนที่เราต้องการ โดยอ้างอิง "โดยค่าเริ่มต้น แต่ฉันอาจจะผิด)


1
มาตรฐาน C ++ 11 ได้รับการสรุป ผมคิดว่ามันถึงเวลาในหัวข้อนี้จะพูดถึงว่ารหัสมากที่สุดควรจะทำอย่างไรดีอย่างสมบูรณ์แบบด้วยการรวมกันของการอ้างอิง, และshared_ptr unique_ptrความหมายการเป็นเจ้าของและอนุสัญญาพารามิเตอร์เข้า / ออกได้รับการดูแลโดยการรวมกันของทั้งสามชิ้นและ const'ness แทบจะไม่จำเป็นต้องใช้ตัวชี้แบบดิบใน C ++ ยกเว้นเมื่อต้องจัดการกับรหัสดั้งเดิมและอัลกอริธึมที่เหมาะสมที่สุด พื้นที่เหล่านั้นที่มีการใช้งานควรมีการห่อหุ้มมากที่สุดเท่าที่จะเป็นไปได้และแปลงพอยน์เตอร์ดิบใด ๆ ให้เทียบเท่ากับ "ทันสมัย" ที่เหมาะสมทางความหมาย
Bret Kuhns

1
ตัวชี้สมาร์ทหลายครั้งไม่ควรผ่าน แต่ควรทดสอบสำหรับ null-ness แล้ววัตถุที่มีอยู่ของพวกเขาผ่านการอ้างอิง ครั้งเดียวที่คุณควรผ่านตัวชี้สมาร์ทคือเมื่อคุณกำลังถ่ายโอน (unique_ptr) หรือการแบ่งปัน (shared_ptr) ความเป็นเจ้าของกับวัตถุอื่น
ลุคเวิร์ ธ

@povman: ฉันเห็นด้วยอย่างเต็มที่: หากความเป็นเจ้าของไม่ได้เป็นส่วนหนึ่งของอินเทอร์เฟซ (และถ้ามันกำลังจะถูกแก้ไขมันไม่ควรจะเป็น) ดังนั้นเราไม่ควรส่งตัวชี้สมาร์ทเป็นพารามิเตอร์ (หรือค่าส่งคืน) สิ่งนั้นซับซ้อนขึ้นเล็กน้อยเมื่อความเป็นเจ้าของเป็นส่วนหนึ่งของส่วนต่อประสาน ตัวอย่างเช่น Sutter / Meyers อภิปรายเกี่ยวกับวิธีส่งผ่าน unique_ptr เป็นพารามิเตอร์: โดยการคัดลอก (Sutter) หรือโดยการอ้างอิง r-value (Meyers)? antipattern อาศัยการส่งผ่านตัวชี้ไปยัง global shared_ptr ทั่วโลกโดยที่ความเสี่ยงของตัวชี้นั้นจะไม่ถูกต้อง (วิธีแก้ปัญหาการคัดลอกตัวชี้อัจฉริยะบนสแต็ก)
paercebal

7

มันไม่ใช่เรื่องของรสนิยม นี่คือกฎบางประการ

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

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

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

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


5

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

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

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

เมื่อคุณต้องการคืนค่าในรูปแบบที่ซับซ้อนฉันมักจะชอบการอ้างอิง ตัวอย่างเช่น:

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

ที่นี่ชื่อฟังก์ชั่นทำให้ชัดเจนว่าคุณได้รับข้อมูลกลับมาในอาร์เรย์ ดังนั้นจึงไม่มีความสับสน

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


4

คัดลอกมาจากwiki -

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

ฉันเห็นด้วย 100% กับสิ่งนี้และนี่คือเหตุผลที่ฉันเชื่อว่าคุณควรใช้การอ้างอิงเมื่อคุณมีเหตุผลที่ดีมากสำหรับการทำเช่นนั้น


ฉันยังเห็นด้วยกับส่วนใหญ่ แต่ฉันมาถึงในมุมมองว่าการสูญเสียการป้องกันในตัวกับ NULL พอยน์เตอร์เป็นบิตแพงเกินไปสำหรับความกังวลด้านไวยากรณ์อย่างหมดจดโดยเฉพาะอย่างยิ่ง - แม้ว่าชัดเจนมากขึ้น - ไวยากรณ์ของตัวชี้ค่อนข้างน่าเกลียด อย่างไรก็ตาม.
connec

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

3

สิ่งที่ควรทราบ:

  1. ชี้สามารถอ้างอิงไม่สามารถNULLNULL

  2. การอ้างอิงใช้งานง่ายขึ้นconstสามารถใช้สำหรับการอ้างอิงเมื่อเราไม่ต้องการเปลี่ยนค่าและเพียงต้องการการอ้างอิงในฟังก์ชั่น

  3. ชี้ใช้กับอ้างอิงในขณะที่ใช้กับ*&

  4. ใช้พอยน์เตอร์เมื่อต้องการการดำเนินการทางคณิตศาสตร์ของพอยน์เตอร์

  5. คุณสามารถมีพอยน์เตอร์เป็นโมฆะint a=5; void *p = &a;แต่ไม่สามารถอ้างอิงถึงโมฆะประเภท

Pointer Vs Reference

void fun(int *a)
{
    cout<<a<<'\n'; // address of a = 0x7fff79f83eac
    cout<<*a<<'\n'; // value at a = 5
    cout<<a+1<<'\n'; // address of a increment by 4 bytes(int) = 0x7fff79f83eb0
    cout<<*(a+1)<<'\n'; // value here is by default = 0
}
void fun(int &a)
{
    cout<<a<<'\n'; // reference of original a passed a = 5
}
int a=5;
fun(&a);
fun(a);

ตัดสินว่าจะใช้เมื่อไร

ตัวชี้ : สำหรับอาร์เรย์, ลิสต์, การใช้งานทรีและการคำนวณทางคณิตศาสตร์

การอ้างอิง : ในพารามิเตอร์ฟังก์ชันและชนิดส่งคืน


2

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

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

ในตอนแรกอาจเป็นความคิดที่ดีที่จะมีพารามิเตอร์ในRefPhone(const SimCard & card)Constructor ที่ส่งผ่านโดยการอ้างอิง มันสนับสนุนการจัดสรรตัวแปรในสแต็กและรับประโยชน์จาก RAII

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

แต่กาลเวลามาทำลายโลกแห่งความสุขของคุณ

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

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

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


มีความแตกต่างดังต่อไปนี้ระหว่างตัวชี้และการอ้างอิง

  1. เมื่อพูดถึงการส่งผ่านตัวแปรการส่งผ่านอ้างอิงดูเหมือนว่าผ่านค่า แต่มีความหมายของตัวชี้ (ทำหน้าที่เหมือนตัวชี้)
  2. การอ้างอิงไม่สามารถเริ่มต้นได้โดยตรงกับ 0 (null)
  3. การอ้างอิง (การอ้างอิงไม่ใช่วัตถุที่อ้างอิง) ไม่สามารถแก้ไขได้ (เทียบเท่ากับตัวชี้ "* const")
  4. การอ้างอิง const สามารถยอมรับพารามิเตอร์ชั่วคราว
  5. การอ้างอิง const ท้องถิ่นยืดอายุการใช้งานของวัตถุชั่วคราว

คำนึงถึงกฎปัจจุบันของฉันมีดังนี้

  • ใช้การอ้างอิงสำหรับพารามิเตอร์ที่จะใช้ภายในเครื่องภายในขอบเขตฟังก์ชัน
  • ใช้พอยน์เตอร์เมื่อ 0 (null) เป็นค่าพารามิเตอร์ที่ยอมรับได้หรือคุณต้องเก็บพารามิเตอร์ไว้เพื่อใช้ต่อไป ถ้า 0 (null) เป็นที่ยอมรับฉันกำลังเพิ่มส่วนต่อท้าย "_n" ในพารามิเตอร์ให้ใช้ตัวชี้ที่มีการป้องกัน (เช่น QPointer ใน Qt) หรือเพียงแค่บันทึก คุณยังสามารถใช้ตัวชี้อัจฉริยะ คุณต้องระวังตัวชี้ที่ใช้ร่วมกันให้รอบคอบยิ่งกว่าตัวชี้ธรรมดา (มิฉะนั้นคุณอาจท้ายด้วยการออกแบบหน่วยความจำรั่วและความรับผิดชอบยุ่ง)

3
ปัญหากับตัวอย่างของคุณไม่ใช่การอ้างอิงที่ไม่ปลอดภัย แต่คุณต้องพึ่งพาบางสิ่งนอกขอบเขตของอินสแตนซ์วัตถุของคุณเพื่อให้สมาชิกส่วนตัวของคุณมีชีวิตอยู่ const SimCard & m_card;เป็นรหัสที่เขียนไม่ดี
plamenko

@plamenko ฉันกลัวว่าคุณจะไม่เข้าใจวัตถุประสงค์ของตัวอย่าง ไม่ว่าconst SimCard & m_cardจะถูกต้องหรือไม่นั้นขึ้นอยู่กับบริบท ข้อความในโพสต์นี้ไม่ได้เป็นการอ้างอิงที่ไม่ปลอดภัย (อาจเป็นไปได้ว่าหากพยายามอย่างหนัก) ข้อความคือคุณไม่ควรติดสุ่มสี่สุ่มห้ากับ "ใช้การอ้างอิงทุกครั้งที่ทำได้" มนต์ ตัวอย่างคือผลลัพธ์ของการใช้ "การอ้างอิงการใช้เมื่อใดก็ตามที่เป็นไปได้" แบบก้าวร้าว ควรมีความชัดเจน
doc

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

คุณไม่ควรมีสมาชิกแบบconst SimCard & m_cardนี้ หากคุณต้องการมีประสิทธิภาพกับขมับเพิ่มคอนexplicit RefPhone(const SimCard&& card)สตรัค
plamenko

@plamenko ถ้าคุณไม่สามารถอ่านด้วยความเข้าใจพื้นฐานบางอย่างคุณมีปัญหาใหญ่กว่าการโพสต์ของฉันที่ทำให้เข้าใจผิด ฉันไม่รู้ว่าฉันจะชัดเจนกว่านี้ได้อย่างไร ดูประโยคแรก มีปัญหากับ "อ้างอิงการใช้งานทุกครั้งที่เป็นไปได้" มนต์! ในโพสต์ของฉันคุณพบข้อความที่อ้างอิงไม่ดี? ในตอนท้ายของโพสต์ของฉันคุณเขียนที่จะใช้อ้างอิงดังนั้นคุณมาพร้อมกับข้อสรุปดังกล่าวได้อย่างไร นี่ไม่ใช่คำตอบสำหรับคำถามโดยตรงหรือไม่
doc

1

ต่อไปนี้เป็นแนวทางบางส่วน

ฟังก์ชั่นใช้ข้อมูลที่ส่งผ่านโดยไม่ต้องแก้ไข:

  1. หากวัตถุข้อมูลมีขนาดเล็กเช่นชนิดข้อมูลในตัวหรือโครงสร้างขนาดเล็กให้ส่งวัตถุนั้นตามค่า

  2. ถ้าวัตถุข้อมูลเป็นอาร์เรย์ให้ใช้ตัวชี้เนื่องจากเป็นตัวเลือกเดียวของคุณ ทำให้ตัวชี้เป็นตัวชี้ไปยัง const

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

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

ฟังก์ชั่นปรับเปลี่ยนข้อมูลในฟังก์ชั่นการโทร:

1. ถ้าวัตถุข้อมูลเป็นชนิดข้อมูลภายในให้ใช้ตัวชี้ หากคุณเห็นรหัสเช่น fixit (& x) โดยที่ x คือ int มันค่อนข้างชัดเจนว่าฟังก์ชั่นนี้ตั้งใจที่จะแก้ไข x

2. ถ้าวัตถุข้อมูลเป็นอาร์เรย์ให้ใช้ตัวเลือกเดียวของคุณ: ตัวชี้

3. ถ้าวัตถุข้อมูลเป็นโครงสร้างให้ใช้การอ้างอิงหรือตัวชี้

4. ถ้าวัตถุข้อมูลเป็นวัตถุคลาสให้ใช้การอ้างอิง

แน่นอนว่านี่เป็นเพียงแนวทางและอาจมีเหตุผลในการตัดสินใจเลือกที่แตกต่างกัน ตัวอย่างเช่น cin ใช้การอ้างอิงสำหรับประเภทพื้นฐานเพื่อให้คุณสามารถใช้ cin >> n แทน cin >> & n


0

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

int *pInt = new int; // allocates *pInt on the Heap

0

ตัวอย่างที่คุณเขียนอย่างถูกต้องควรมีลักษณะ

void add_one(int& n) { n += 1; }
void add_one(int* const n)
{
  if (n)
    *n += 1;
}

นั่นเป็นเหตุผลว่าทำไมการอ้างอิงถึงเป็นที่นิยมมากกว่าถ้าเป็นไปได้ ...


-1

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

ตอนนี้ในหัวข้อของพอยน์เตอร์ vs การอ้างอิง IMHO ฉันคิดว่าความชัดเจนเหนือสิ่งอื่นใด ทันทีที่ฉันอ่านพฤติกรรมโดยปริยายนิ้วเท้าของฉันก็เริ่มม้วนงอ ฉันยอมรับว่ามันเป็นพฤติกรรมที่ดีโดยปริยายซึ่งการอ้างอิงไม่สามารถเป็นค่า NULL ได้

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

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


-2

สำหรับพอยน์เตอร์คุณต้องการให้ชี้ไปที่บางสิ่งดังนั้นพอยน์เตอร์จึงใช้พื้นที่หน่วยความจำ

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

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


4
Nope ฟังก์ชั่นที่ใช้เป็นตัวชี้ไม่จำเป็นต้องมีการจัดสรรของตัวแปรชี้: &addressคุณสามารถส่งผ่านชั่วคราว การอ้างอิงจะทำให้หน่วยความจำมีราคาอย่างแน่นอนหากเป็นสมาชิกของออบเจ็กต์และนอกจากนี้คอมไพเลอร์ที่ยังหลงเหลืออยู่ทั้งหมดจะใช้การอ้างอิงเป็นที่อยู่ดังนั้นคุณจึงไม่ต้องบันทึกอะไรเลยในแง่ของการส่งผ่านพารามิเตอร์
underscore_d
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.