ทำไมถึงไม่เป็นโมฆะ * `` หล่อใน C ++ โดยนัย?


30

ใน C ไม่จำเป็นต้องส่ง a void *ไปยังชนิดพอยน์เตอร์ใด ๆ มันได้รับการเลื่อนอย่างปลอดภัยเสมอ อย่างไรก็ตามใน C ++ นี่ไม่ใช่กรณี เช่น,

int *a = malloc(sizeof(int));

ทำงานใน C แต่ไม่ใช่ใน C ++ (หมายเหตุ: ฉันรู้ว่าคุณไม่ควรใช้mallocใน C ++ หรือสำหรับเรื่องnewนั้นและควรเลือกสมาร์ทพอยน์เตอร์และ / หรือ STL แทนที่จะถามสิ่งนี้เพราะความอยากรู้อยากเห็น) ทำไมมาตรฐาน C ++ จึงไม่อนุญาตให้ใช้แบบจำลองนี้ ในขณะที่มาตรฐาน C ทำอะไร


3
long *a = malloc(sizeof(int));อ๊ะมีคนลืมเปลี่ยนประเภทเดียว!
Doval

4
@Doval: นั่นยังคงได้รับการแก้ไขได้อย่างง่ายดายโดยใช้sizeof(*a)แทน
wolfPack88

3
ฉันเชื่อว่าจุดของ @ ratchetfreak คือเหตุผลที่ C ทำการแปลงโดยปริยายนี้เพราะmallocไม่สามารถส่งกลับตัวชี้ไปยังประเภทที่จัดสรร newคือ C ++ จะส่งคืนตัวชี้ไปยังประเภทที่จัดสรรดังนั้นโค้ด C ++ ที่เขียนอย่างถูกต้องจะไม่มีvoid *การส่งต่อ
Gort the Robot

3
นอกเหนือจากนี้มันไม่ได้เป็นตัวชี้ประเภทอื่น ๆ ต้องใช้พอยน์เตอร์ข้อมูลเท่านั้น
Deduplicator

3
@ wolfPack88 คุณมีประวัติที่ไม่ถูกต้อง C ++ มีvoid, C ทำไม่ได้ เมื่อคำหลัก / แนวคิดนั้นถูกเพิ่มใน C พวกเขาเปลี่ยนมันเพื่อให้เหมาะกับความต้องการของ C นั่นคือไม่นานหลังจากที่ประเภทตัวชี้เริ่มต้นการตรวจสอบในทุก ดูว่าคุณสามารถหาเอกสารอธิบาย K&R C ทางออนไลน์หรือสำเนาแบบดั้งเดิมของข้อความการเขียนโปรแกรม C เช่น Waite Group C Primer ANSI C นั้นเต็มไปด้วยคุณสมบัติที่ได้รับแรงบันดาลใจจาก C ++ และ K&R C นั้นง่ายกว่ามาก ดังนั้นมันจึงถูกต้องมากขึ้นที่ C ++ จะขยาย C ตามที่มีอยู่ในเวลานั้นและ C ที่คุณรู้ว่าถูกถอดจาก C ++
JDługosz

คำตอบ:


39

เนื่องจากการแปลงประเภทโดยนัยมักจะไม่ปลอดภัยและ C ++ ใช้ท่าทางที่ปลอดภัยกว่าในการพิมพ์มากกว่า C

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

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

คุณอาจถามว่ามันเป็นมิตรได้อย่างไรเมื่อต้องการให้คุณพิมพ์เพิ่มเติม

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

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

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


C เป็นขั้นตอนเดียวเหนือรหัสเครื่อง ฉันสามารถให้อภัย C สำหรับสิ่งนี้
Qix

8
โปรแกรม (ไม่ว่าจะใช้ภาษาใด) จะต้องอ่าน การปฏิบัติการที่ชัดเจนโดดเด่น
Matthieu M.

10
มันเป็นมูลค่า noting ที่ปลดเปลื้องผ่านvoid*มีมากขึ้นที่ไม่ปลอดภัยใน C ++ เพราะมีวิธีการคุณสมบัติ OOP บางอย่างจะดำเนินการใน C ++ ชี้ไปยังวัตถุเดียวกันอาจมีค่าที่แตกต่างกันขึ้นอยู่กับชนิดของตัวชี้
hyde

@MatthieuM จริงแท้แน่นอน. ขอบคุณสำหรับการเพิ่มที่มันคุ้มค่าที่จะเป็นส่วนหนึ่งของคำตอบ
Mike Nakis

1
@MatthieuM .: อา แต่คุณไม่ต้องการทำทุกอย่างอย่างชัดเจน ความสามารถในการอ่านไม่ได้รับการปรับปรุงโดยการอ่านให้มากขึ้น แม้ว่าในจุดนี้ความสมดุลจะชัดเจนสำหรับการชัดเจน
Deduplicator

28

นี่คือสิ่งที่Stroustrup พูดว่า :

ใน C คุณสามารถแปลงเป็นโมฆะ * เป็น T * โดยปริยาย สิ่งนี้ไม่ปลอดภัย

จากนั้นเขาก็แสดงตัวอย่างว่าโมฆะ * เป็นอันตรายได้อย่างไรและพูดว่า:

... ดังนั้นใน C ++ เพื่อให้ได้ T * จากโมฆะ * คุณต้องใช้การส่งที่ชัดเจน ...

ในที่สุดเขาบันทึก:

หนึ่งในการใช้งานทั่วไปของการแปลงที่ไม่ปลอดภัยนี้ใน C คือการกำหนดผลลัพธ์ของ malloc () ให้กับตัวชี้ที่เหมาะสม ตัวอย่างเช่น:

int * p = malloc (sizeof (int));

ใน C ++ ให้ใช้โอเปอเรเตอร์ตัวใหม่ typesafe:

int * p = new int;

เขาเข้าไปในรายละเอียดมากขึ้นเกี่ยวกับเรื่องนี้ในการออกแบบและวิวัฒนาการของภาษา C ++

ดังนั้นคำตอบก็คือ: นักออกแบบภาษาเชื่อว่ามันเป็นรูปแบบที่ไม่ปลอดภัยและทำให้ผิดกฎหมายและให้วิธีการอื่นในการบรรลุสิ่งที่รูปแบบที่ใช้ตามปกติ


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

นี่เป็นคำตอบที่ดีมากดีกว่าของฉัน ฉันไม่ชอบวิธีการ "โต้แย้งจากผู้มีอำนาจ" เพียงเล็กน้อยเท่านั้น
Mike Nakis

"new int" - ว้าวเป็นมือใหม่ C ++ ฉันมีปัญหาในการคิดวิธีการเพิ่มประเภทฐานไปยังกองและฉันไม่ได้รู้ว่าคุณสามารถทำได้ -1 ใช้สำหรับ malloc
Katana314

3
@ MikeNakisFWIW ความตั้งใจของฉันคือเติมเต็มคำตอบของคุณ ในกรณีนี้คำถามคือ "ทำไมพวกเขาถึงทำอย่างนั้น" ดังนั้นฉันคิดว่าการได้ยินจากหัวหน้านักออกแบบนั้นเป็นธรรม
Gort the Robot

11

ใน C ไม่จำเป็นต้องใช้โมฆะ * กับตัวชี้ประเภทอื่น ๆ มันคือการส่งเสริมอย่างปลอดภัยเสมอ

มันได้รับการส่งเสริมเสมอใช่ แต่แทบจะไม่ปลอดภัยได้อย่างปลอดภัย

C ++ ปิดใช้งานลักษณะการทำงานนี้อย่างแม่นยำเนื่องจากพยายามมีระบบประเภทที่ปลอดภัยกว่า C และพฤติกรรมนี้ไม่ปลอดภัย


พิจารณาโดยทั่วไป 3 วิธีในการพิมพ์การแปลง:

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

1 เป็นสิ่งที่น่าเกลียดและเป็นอุปสรรคต่อการทำสิ่งต่าง ๆ แต่จริง ๆ แล้วอาจถูกนำมาใช้ในกรณีที่จำเป็นต้องมีการดูแลที่ดี C เลือกแบบคร่าวๆสำหรับ 2 ซึ่งง่ายต่อการนำไปใช้และ C ++ สำหรับ 3 ซึ่งยากต่อการนำไปใช้ แต่ปลอดภัยกว่า


มันเป็นถนนสายยาวที่ยาวถึง 3 โดยมีข้อยกเว้นเพิ่มเติมในภายหลังและแม่แบบยังคงอยู่ในภายหลัง
JDługosz

ดีแม่ไม่ได้จริงๆที่จำเป็นสำหรับระบบดังกล่าวประเภทความปลอดภัยที่สมบูรณ์: Hindley-มิลเนอร์ภาษาตามได้รับพร้อมโดยที่พวกเขาค่อนข้างดีโดยไม่ต้องแปลงนัยที่ทุกคนและไม่จำเป็นต้องเขียนออกประเภท explictly อย่างใดอย่างหนึ่ง (แน่นอนภาษาเหล่านี้ขึ้นอยู่กับประเภทการลบ / การเก็บขยะ / polymorphism ชนิดที่สูงกว่าซึ่งเป็นสิ่งที่ C ++ ค่อนข้างหลีกเลี่ยง)
leftaroundabout

1

ตามคำจำกัดความตัวชี้โมฆะสามารถชี้ไปที่อะไรก็ได้ ตัวชี้ใด ๆ ที่สามารถแปลงเป็นตัวชี้โมฆะและดังนั้นคุณจะสามารถแปลงกลับมาถึงค่าเดียวกัน อย่างไรก็ตามตัวชี้ไปยังประเภทอื่นอาจมีข้อ จำกัด เช่นข้อ จำกัด การจัดตำแหน่ง ตัวอย่างเช่นลองนึกภาพสถาปัตยกรรมที่อักขระสามารถครอบครองที่อยู่หน่วยความจำใด ๆ ได้ แต่จำนวนเต็มจะต้องเริ่มจากขอบเขตที่อยู่ ในสถาปัตยกรรมบางอย่างพอยน์เตอร์จำนวนเต็มอาจนับ 16, 32 หรือ 64 บิตในแต่ละครั้งเพื่อให้ char * อาจมีค่าตัวเลขหลายค่าของ int * ในขณะที่ชี้ไปที่สถานที่เดียวกันในหน่วยความจำ ในกรณีนี้การแปลงจากช่องว่าง * จริง ๆ แล้วจะปัดเศษบิตซึ่งไม่สามารถกู้คืนได้และดังนั้นจึงไม่สามารถย้อนกลับได้

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.