การปฏิบัติที่แย่ที่สุดใน C ++, ข้อผิดพลาดทั่วไป [ปิด]


35

หลังจากอ่านคำพูดที่โด่งดังของ Linus Torvaldsฉันสงสัยว่าจริงๆแล้วอะไรคือข้อผิดพลาดทั้งหมดของโปรแกรมเมอร์ใน C ++ ฉันไม่ได้อ้างถึง typos หรือโปรแกรมที่ไม่ดีตามที่ได้รับการรักษาในคำถามนี้และคำตอบของมันแต่สำหรับข้อผิดพลาดระดับสูงที่ไม่ได้ตรวจพบโดยคอมไพเลอร์และไม่ส่งผลให้เกิดข้อบกพร่องที่เห็นได้ชัดในตอนแรก สิ่งที่ไม่น่าจะเป็นใน C แต่น่าจะเกิดขึ้นใน C ++ โดยผู้มาใหม่ที่ไม่เข้าใจความหมายของรหัสทั้งหมด

ฉันยังยินดีต้อนรับคำตอบที่ชี้ให้เห็นถึงประสิทธิภาพที่ลดลงอย่างมากซึ่งมักจะไม่เกิดขึ้น ตัวอย่างของสิ่งที่อาจารย์คนหนึ่งของฉันเคยบอกฉันเกี่ยวกับตัวแยกวิเคราะห์ LR (1) ที่ฉันเขียน:

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


6
ข้อผิดพลาดที่น่ารังเกียจบางอย่างที่คุณสามารถทำได้ใน C / C ++ นั้นส่วนใหญ่เป็นเพราะมรดกทางวัฒนธรรม ... อ่านพฤติกรรมที่ไม่ได้กำหนดการจัดการหน่วยความจำด้วยตนเอง ฯลฯ นอกจากนี้คำแนะนำของศาสตราจารย์ดูเหมือนว่าปลอม / ผิด (สำหรับฉัน ผู้เชี่ยวชาญ C ++) - การสร้างอินสแตนซ์เท็มเพลตควรให้คลาสปกติพร้อม vtable สำหรับvirtualฟังก์ชั่นใช่มั้ย

8
คุณอาจจำสิ่งที่อาจารย์ของคุณพูดผิดหรือเขาไม่มีเงื่อนงำในสิ่งที่เขาพูดถึง คลาสที่ได้รับมาไม่จำเป็นต้องใช้ RTTI (การสะท้อนกลับ AKA) เพื่อค้นหาสิ่งต่าง ๆ หากพวกเขากำลังใช้วิธีเสมือนรหัสอาจต้องทำการค้นหา vtable สำหรับการแจกจ่าย แต่นั่นแปลเป็นคำสั่ง ASM เดียวในโปรเซสเซอร์จำนวนมาก เนื่องจากปัญหาการแคชอาจทำให้สิ่งต่าง ๆ ช้าลงด้วยจำนวนหนึ่ง แต่คุณไม่น่าจะสังเกตเห็นค่าใช้จ่ายในกรณีการใช้งานที่ต้องการมากที่สุด มีเหตุผลดีๆมากมายที่ควรหลีกเลี่ยง C ++ แต่การค้นหา vtable ไม่ใช่หนึ่งในนั้น
Mason Wheeler

5
@ FelixDombek: กล่าวโดยทั่วไปและนำไปใช้ทั่วกระดานข้อความที่อ้างจากอาจารย์ของคุณเพียงแค่แสดงความไม่รู้จำนวนมาก เมื่อการออกแบบของคุณต้องการความแปรปรวนแบบรันไทม์บางชนิดการใช้ฟังก์ชันเสมือนมักเป็นตัวเลือกที่ดีที่สุด เมื่อคุณไม่ต้องการใช้มันอย่าใช้มัน: คุณไม่จำเป็นต้องใช้เมธอดทั้งหมดเสมือนเพราะคุณใช้คลาสที่ได้รับมา
Fred Nurk

5
@Mason Wheeler: RTTI มีข้อมูลเกี่ยวกับประเภทเพียงพอที่จะสามารถตรวจสอบว่า a dynamic_castควรจะประสบความสำเร็จหรือไม่และสิ่งอื่น ๆ น้อย แต่การสะท้อนครอบคลุมมากขึ้นรวมถึงความสามารถในการดึงข้อมูลเกี่ยวกับคุณสมบัติหรือฟังก์ชั่นสมาชิกที่ไม่ได้ อยู่ใน C ++
David Rodríguez - dribeas

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

คำตอบ:


69

Torvalds กำลังพูดถึงลาของเขาที่นี่


ตกลงทำไมเขาพูดถึงลาของเขา:

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

สำหรับสิ่งที่อาจเป็นแพรวพราวเป็นจุดมุ่งหมายมากที่สุดของเขา:

  • STL และ Boost เป็นอึมากที่สุด <- อะไรก็ตาม คุณเป็นคนงี่เง่า
  • STL และ Boost ทำให้เกิดอาการปวดจำนวนไม่ จำกัด <- ไร้สาระ เห็นได้ชัดว่าเขาจงใจมากเกินไป แต่แล้วอะไรคือคำพูดที่แท้จริงของเขาที่นี่? ฉันไม่รู้ มีบางอย่างที่ยากเกินกว่าจะคิดออกปัญหาเมื่อคุณทำให้คอมไพเลอร์อาเจียนด้วยจิตวิญญาณหรือบางสิ่งบางอย่าง แต่ก็ไม่ยากที่จะคิดออกมากกว่าการดีบั๊ก UB ที่เกิดจากการใช้โครงสร้าง C ในทางที่ผิด *
  • ตัวแบบนามธรรมสนับสนุนโดย C ++ นั้นไม่มีประสิทธิภาพ <- ชอบอะไรนะ? เขาไม่เคยขยายตัวไม่เคยให้ตัวอย่างใด ๆ กับสิ่งที่เขาหมายถึงเขาแค่บอกว่า BFD เนื่องจากฉันไม่สามารถบอกได้ว่าเขาหมายถึงอะไรมีความพยายามเล็กน้อยในการ "โต้แย้ง" ข้อความ มันเป็นมนต์ทั่วไปของ C bigots แต่นั่นไม่ได้ทำให้มันเป็นที่เข้าใจหรือเข้าใจได้มากขึ้น
  • การใช้ C ++ อย่างถูกต้องหมายความว่าคุณ จำกัด ตัวเองในด้าน C <- จริง ๆ แล้ว WORSE C ++ มีรหัสนี้อยู่ที่นั่นดังนั้นฉันจึงยังไม่รู้ WTF ที่เขาพูดถึง

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

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


ตอบคำถามจริง:

อาจเป็นสิ่งที่เลวร้ายที่สุดและที่พบบ่อยที่สุดคือการปฏิบัติ C ++ ที่ไม่ดีคือการปฏิบัติต่อ C เช่นการใช้ฟังก์ชั่น C API อย่างต่อเนื่องเช่น printf, รับ (ยังถือว่าไม่ดีใน C), strtok ฯลฯ ... ไม่เพียง แต่ล้มเหลว โดยระบบประเภทที่เข้มงวดมากขึ้นพวกเขานำไปสู่ภาวะแทรกซ้อนเพิ่มเติมอย่างต่อเนื่องเมื่อพยายามที่จะโต้ตอบกับรหัส "ของจริง" C ++ โดยทั่วไปให้ทำตรงข้ามกับสิ่งที่ Torvalds กำลังให้คำแนะนำ

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

ไม่ต้องบอกว่า C ไม่ค่อยดีเท่าไหร่ แน่นอนว่าฉันชอบ C ++ ที่ดีกว่า โปรแกรมเมอร์ C ชอบ C ดีกว่า มีการแข่งขันและชอบเล่นตามอัตวิสัย นอกจากนี้ยังมีข้อมูลที่ผิดมากมายและ FUD ลอยอยู่รอบ ๆ ฉันจะบอกว่ามี FUD และข้อมูลที่ผิดมากขึ้นประมาณ C ++ แต่ฉันลำเอียงในเรื่องนี้ ตัวอย่างเช่นปัญหา "bloat" และ "performance" C ++ ที่คาดการณ์ไว้นั้นไม่ได้เป็นประเด็นหลักที่แท้จริงโดยส่วนใหญ่แล้วจะถูกเป่าออกจากสัดส่วนของความเป็นจริง

สำหรับประเด็นที่อาจารย์ของคุณอ้างถึงสิ่งเหล่านี้ไม่ได้เป็นเอกลักษณ์ของ C ++ ใน OOP (และในการเขียนโปรแกรมทั่วไป) คุณต้องการที่จะแต่งเพลงมากกว่าการสืบทอด การสืบทอดคือความสัมพันธ์ที่คึกคักที่สุดที่มีในภาษา OO ทั้งหมด C ++ เพิ่มอีกหนึ่งที่แข็งแกร่งมิตรภาพ ควรใช้การสืบทอดหลายรูปแบบเพื่อแทนนามธรรมและความสัมพันธ์ "is-a" ไม่ควรนำมาใช้ซ้ำ นี่เป็นข้อผิดพลาดที่ใหญ่เป็นอันดับสองที่คุณสามารถทำได้ใน C ++ และมันค่อนข้างใหญ่ แต่ก็ยังห่างไกลจากความเป็นเอกลักษณ์ของภาษา คุณสามารถสร้างความสัมพันธ์การสืบทอดที่ซับซ้อนมากเกินไปใน C # หรือ Java เช่นกันและพวกเขาจะมีปัญหาเดียวกัน


1
แดกดันจนกระทั่งหลังจากปี 2550 ระบบคอมไพล์ก็ใช้ลีนุกซ์เวอร์ชั่นที่พกพาได้เท่านั้น. ระบบใด ๆ ที่เหมือนยูนิกซ์ จากนั้นอีกครั้งเนื่องจากสถานการณ์ที่นำไปสู่การสร้างคอมไพล์ฉันไม่ได้ถือมันไว้กับเขาอย่างแน่นอน
Chris K

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

19

ฉันคิดเสมอว่าอันตรายของ C ++ นั้นเกินความจริงโดย C ที่ไม่มีประสบการณ์ C กับโปรแกรมเมอร์ Classes

ใช่ C ++ นั้นยากที่จะรับได้มากกว่าอย่าง Java แต่ถ้าคุณใช้โปรแกรมด้วยเทคนิคที่ทันสมัยมันค่อนข้างง่ายที่จะเขียนโปรแกรมที่มีประสิทธิภาพ ผมไม่ได้มีที่ยากมากขึ้นของการเขียนโปรแกรมเวลาใน C ++ กว่าที่ฉันทำในภาษาเช่น Java, และฉันมักจะพบว่าตัวเองหายไปบาง c ++ นามธรรมเช่นแม่แบบและ RAII เมื่อฉันออกแบบในภาษาอื่น ๆ

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

ตอนนี้ใน C ++ ที่ทันสมัยโดยปกติแล้วคุณไม่จำเป็นต้องสนใจเกี่ยวกับวัตถุตลอดชีวิต คุณมีตัวทำลายและพอยน์เตอร์อัจฉริยะที่จัดการอายุการใช้งานของวัตถุให้คุณ 99% ของเวลาใช้งานได้อย่างยอดเยี่ยม แต่ทุกขณะนี้จากนั้นคุณจะได้รับการเมาโดยตัวชี้ห้อย (หรือการอ้างอิง) ตัวอย่างเช่นเมื่อเร็ว ๆ นี้ฉันมีวัตถุ (เรียกว่าFoo) ซึ่งมีตัวแปรอ้างอิงภายในไปยังวัตถุอื่น (เรียกว่าBar) เมื่อมาถึงจุดหนึ่งผมโง่จัดสิ่งเพื่อให้Barออกไปจากขอบเขตก่อนFooแต่Foo's destructor Barจบลงด้วยการเรียกฟังก์ชั่นสมาชิกของ จำเป็นต้องพูดสิ่งต่าง ๆ กลับไม่ดี

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


* ถ้าทรัพยากรที่ถูกจัดการเป็นหน่วยความจำนั่นคือ


8
ไม่ต้องสนใจเรื่องอายุการใช้งานของวัตถุใน Java และ C # หรือไม่ GC ของพวกเขาดูแลหน่วยความจำ แต่นั่นเป็นเพียงส่วนเล็ก ๆ ของ RAII สำหรับฉัน ดูอินเทอร์เฟซ "ใช้แล้วทิ้ง" ที่ภาษาเหล่านั้นมี
Fred Nurk

การดูแลเกี่ยวกับอายุการใช้งานของวัตถุจะหายากใน Java ยกเว้นการออกแบบที่ไม่สะดวกของไลบรารี I / O
dan04

ปัญหาอ้างอิงที่ห้อยต่องแต่งของคุณเป็นสิ่งที่ฉันสนใจที่จะพยายามแก้ไข ฉันเริ่มการสนทนาในบล็อกของฉันเกี่ยวกับทิศทางที่ฉันกำลังมุ่งแก้ปัญหา (ตัวชี้สัญญา) โดยทั่วไปฉันคิดว่าภาษาสามารถใช้พอยน์เตอร์อัจฉริยะอีกสองสามตัว มีส่วนร่วมในการอภิปรายหากคุณสนใจ ไม่มีใครเลยที่เป็นอย่างนั้น ... แต่ถ้ามันเป็นสิ่งที่คุณต้องการเห็นการแก้ไข ... ฉันพบปัญหามากกว่า 10% ของเวลา
Edward Strange

13

ความแตกต่างในรหัสมักจะเกี่ยวข้องกับโปรแกรมเมอร์มากกว่าภาษา โดยเฉพาะอย่างยิ่งโปรแกรมเมอร์ C ++ ที่ดีและโปรแกรมเมอร์ C จะมาพร้อมกับโซลูชันที่ดี (แม้ว่าจะแตกต่างกัน) ในทำนองเดียวกัน ตอนนี้ C เป็นภาษาที่ง่ายกว่า (เป็นภาษา) และนั่นหมายความว่ามี abstractions น้อยลงและมองเห็นสิ่งที่รหัสทำ

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


3
ราคาของการวนซ้ำในการstd::vector<bool>เปลี่ยนแปลงแต่ละค่าเป็นเท่าใด for ( std::vector<bool>::iterator it = v.begin(), end = v.end(); it != end; ++it ) { *it = !*it; }? สิ่งที่เป็นนามธรรมออกไป*it = !*it;?
David Rodríguez - dribeas

2
แม้ว่ามันอาจไม่เป็นธรรมที่จะเลือกในสิ่งที่น่าสะอิดสะเอียนภาษาเฉพาะการวิพากษ์วิจารณ์อย่างกว้างขวางว่าเป็นความผิดพลาด ...
เฟร็ด Nurk

2
@ เฟร็ด Nurk: std::vector<bool>เป็นความผิดพลาดที่รู้จักกันดี แต่มันเป็นตัวอย่างที่ดีจริงๆของสิ่งที่ถูกกล่าวถึง: abstractions ดี แต่คุณต้องระวังสิ่งที่พวกเขาซ่อน สามารถเดียวกันและจะเกิดขึ้นในรหัสผู้ใช้ สำหรับ starters, ฉันได้เห็นทั้งใน C ++ และผู้คนจาวาโดยใช้ข้อยกเว้นในการดำเนินการควบคุมการไหลและรหัสที่มีลักษณะเหมือนการเรียกฟังก์ชันการทำรังที่เป็นจริง bailout ปล่อยยกเว้น: นำมาใช้เป็นvoid endOperation(); throw EndOperation;โปรแกรมเมอร์ที่ดีจะหลีกเลี่ยงการสร้างที่น่าแปลกใจแต่ความจริงก็คือคุณสามารถค้นหาได้
David Rodríguez - dribeas

5
หนึ่งในประเด็นของ Torvalds คือ: เขาสามารถขับผู้เริ่มต้นออกไปได้เพียงแค่เลือก C มากกว่า C ++ (ดูเหมือนว่าจะเป็นผู้เริ่มต้น C ++ มากกว่า) และ C ++ นั้นซับซ้อนกว่ามีเส้นโค้งการเรียนรู้ที่ชันกว่าและมีโอกาสมากขึ้น .
David Rodríguez - dribeas

2
+1 นี่คือสิ่งที่ไลนัสบ่นเกี่ยวกับ เขาออกมาต่อต้าน c-c ++ แต่นั่นไม่ใช่อย่างนั้น เขาเป็นเพียงแอนตี้ - ซีพลัส - โปรแกรมเมอร์
greyfade

13

try/catchบล็อกมากเกินไป

File file("some.txt");
try
{
  /**/

  file.close();
}
catch(std::exception const& e)
{
  file.close();
}

สิ่งนี้มักเกิดจากภาษาเช่น Java และผู้คนจะยืนยันว่า C ++ ขาดfinalizeประโยค

แต่รหัสนี้มีสองประเด็น:

  • หนึ่งต้องสร้างfileก่อนtry/catchเพราะคุณไม่สามารถจริงไฟล์ที่ไม่อยู่ในclose catchสิ่งนี้นำไปสู่ ​​"การรั่วไหลของขอบเขต" ในสิ่งที่fileมองเห็นได้หลังจากที่ถูกปิด คุณสามารถเพิ่มบล็อกได้ แต่ ... : /
  • หากมีใครบางคนเข้ามาและเพิ่ม a returnในtryขอบเขตแล้วไฟล์จะไม่ถูกปิด (ซึ่งเป็นสาเหตุที่ผู้คนมักจะบ่นเกี่ยวกับการขาดfinalizeประโยค)

อย่างไรก็ตามใน C ++ เรามีวิธีที่มีประสิทธิภาพมากขึ้นในการจัดการกับปัญหานี้ที่:

  • ของ Java finalize
  • C # 's using
  • กลับเป็น defer

เรามี RAII ซึ่งมีคุณสมบัติที่น่าสนใจจริง ๆ จะสรุปได้ดีที่สุดSBRM(การจัดการทรัพยากรที่ จำกัด ขอบเขต)

โดยการกำหนดคลาสเพื่อให้ผู้ทำลายล้างทรัพยากรที่เป็นเจ้าของเราไม่ได้ใส่ความรับผิดชอบในการจัดการทรัพยากรให้กับผู้ใช้ทุกคน!

นี่คือคุณลักษณะผมพลาดในภาษาอื่น ๆ และอาจจะเป็นหนึ่งที่ถูกลืมมากที่สุด

ความจริงก็คือไม่จำเป็นต้องเขียนtry/catchบล็อกใน C ++ บ่อยนักแยกระดับสูงสุดเพื่อหลีกเลี่ยงการเลิกจ้างโดยไม่ต้องเข้าสู่ระบบ


1
ฉันไม่คิดว่ามันเป็นอิทธิพลของ Java มากเท่ากับ C. (คุณสามารถทดแทนได้โดยตรงfopenและfcloseที่นี่) RAII เป็นวิธีที่ "เหมาะสม" ในการทำสิ่งต่าง ๆ ที่นี่ แต่ไม่สะดวกสำหรับผู้ที่ต้องการใช้ C ไลบรารีจาก C ++ .
dan04

สำหรับคำตอบประเภทนี้การให้ตัวอย่างของวิธีการแก้ไขที่ถูกต้องน่าจะเหมาะสม
Claus Jørgensen

@ ClausJørgensen: การแก้ปัญหาน่าเสียดายที่ไม่ใช่ "ฉูดฉาด" เพราะมันเกี่ยวข้องกับเพียงFile file("some.txt");และนั่นคือ (ไม่openไม่closeไม่ไม่try... )
Matthieu M.

D ยังมี RAII
Demi

@Demetri: ฉันไม่คุ้นเคยกับ D คุณสามารถอธิบายได้ว่า RAII โต้ตอบกับ Garbage Collection อย่างไร ฉันรู้ว่าใน Python คุณสามารถเขียนวิธีการ "deinit" อย่างไรก็ตามเอกสารเตือนว่าในกรณีของรอบของการอ้างอิงวัตถุบางอย่างจะไม่เห็นวิธีการ deinit ของพวกเขาที่เรียกว่า
Matthieu M.

9

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

น่าเสียดายที่ C ++ เต็มไปด้วย gotchas 'ที่ซ่อนอยู่' เช่นนี้ แต่การบ่นเกี่ยวกับมันก็เหมือนกับการที่คุณไปฝรั่งเศสและไม่เข้าใจสิ่งที่ผู้คนพูด หากคุณกำลังจะไปที่นั่นเรียนรู้ภาษา


1
ฉันคิดว่าปัญหาเกี่ยวกับ C ++ นั้นง่ายมากที่จะยิงตัวคุณเอง แน่นอนว่ามีโปรแกรมเมอร์ C ++ ที่ดีอยู่ทั่วซอฟต์แวร์ที่ดีจำนวนมากที่เขียนใน C ++ แต่มันยากมากที่จะเป็นนักพัฒนา C ++ ที่ดี ซีรี่ส์ C ++ ของ Scott Meyers แสดงให้เห็นถึงรายละเอียดปลีกย่อยของภาษา
Marco Mustapic

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

อย่างน้อยสิ่งนี้กำลังดีขึ้นด้วยกฎที่ค่อนข้างเข้มงวดใหม่ในการสร้างการคัดลอก / ย้ายโดยปริยายใน C ++ 0x ในหลายกรณีที่มีการละเมิดกฎของสามกรณีการสร้างสำเนาโดยนัยจะถูกคัดค้านและควรสร้างคำเตือน
sellibitze

6

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

มันจะต้องมีการเรียนรู้และเข้าใจอย่างถูกต้องเพียงแค่เรียนรู้โดยการทำ (หรือใช้อย่างที่ใครคนหนึ่งจะใช้ภาษาอื่น) จะนำไปสู่รหัสที่ไม่มีประสิทธิภาพและเกิดข้อผิดพลาดได้ง่าย


4

ดี ... สำหรับผู้เริ่มต้นคุณสามารถอ่านC ++ FAQ Lite ได้

จากนั้นหลายคนได้สร้างอาชีพที่เขียนหนังสือเกี่ยวกับความซับซ้อนของ C ++:

Herb Sutterและ Scott Meyersได้แก่

สำหรับการพูดจาโผงผางที่ขาดไปของ Torvalds ... เกิดขึ้นกับผู้คนอย่างจริงจัง: ไม่มีภาษาอื่นออกมาที่นั่นเลยที่มีหมึกหกล้นเหลืออยู่ในการรับมือกับความแตกต่างของภาษา หนังสือ Python & Ruby & Java ของคุณทุกคนมุ่งเน้นไปที่การเขียนแอปพลิเคชัน ... หนังสือ C ++ ของคุณมุ่งเน้นไปที่คุณสมบัติ / เคล็ดลับ / กับภาษาโง่ ๆ


1
อืม ... javapuzzlers.com , jimbrooks.org/web/python/#Pitfalls ผมว่าเร่ง c ++ (สำหรับตัวอย่างหนึ่ง) มุ่งเน้นมากเพิ่มเติมเกี่ยวกับวิธีการเขียนโค้ดกว่านี้ทำ ...
เจอร์รี่โลงศพ

1
คุณได้ยกตัวอย่างบางส่วนของทรัพยากรที่ชี้ให้เห็นกรณีและปัญหาในภาษาของตน สิ่งที่ดูแปลก ๆ และคุณก็ค่อนข้างแน่ใจว่ามันทำงานอย่างไร (แม้ว่ารายการของ python นั้นอยู่ใกล้) ... C ++ มีทั้งอุตสาหกรรมชี้ให้เห็นสิ่งที่ดูสมบูรณ์แบบที่ทำงานในแบบที่คุณไม่คาดคิด
ดินสีแดง

3

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

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


8
การดูแลรักษารหัสความซับซ้อนในการเผชิญกับการเปลี่ยนแปลงความต้องการเสมอทำให้เกิดข้อผิดพลาดได้โดยไม่ต้องใช้ความพยายามมากไม่มีอะไรพิเศษโดยเฉพาะอย่างยิ่งเกี่ยวกับแม่มี
Fred Nurk

2

คำเตือน: นี่ไม่ใช่คำตอบที่เกือบจะเป็นคำวิจารณ์ในการพูดคุยว่า "ผู้ใช้ไม่ทราบ" เชื่อมโยงกับคำตอบของเขา

ประเด็นหลักแรกของเขาคือ (ที่คาดคะเน) "เคยเปลี่ยนมาตรฐาน" ในความเป็นจริงตัวอย่างที่เขามอบให้ทั้งหมดเกี่ยวข้องกับการเปลี่ยนแปลงใน C ++ ก่อนที่จะมีมาตรฐาน ตั้งแต่ปี 1998 (เมื่อมาตรฐาน C ++ แรกสิ้นสุด) การเปลี่ยนแปลงภาษามีน้อยมาก - อันที่จริงหลายคนแย้งว่าปัญหาที่แท้จริงคือควรมีการเปลี่ยนแปลงมากขึ้น ฉันแน่ใจว่ารหัสทั้งหมดที่สอดคล้องกับมาตรฐาน C ++ ดั้งเดิมยังคงสอดคล้องกับมาตรฐานปัจจุบัน แม้ว่าจะค่อนข้างแน่ใจน้อยกว่ายกเว้นบางสิ่งที่เปลี่ยนแปลงอย่างรวดเร็ว (และค่อนข้างคาดไม่ถึง) ก็จะเป็นจริงอย่างมากกับมาตรฐาน C ++ ที่กำลังจะมาถึงเช่นกัน (ในทางทฤษฎีรหัสทั้งหมดที่ใช้exportจะแตกสลาย แต่ไม่มีอยู่จริง จากมุมมองที่ใช้งานจริงมันไม่ใช่ปัญหา) ฉันสามารถนึกถึงภาษาอื่น ๆ ไม่กี่ระบบปฏิบัติการ (หรือคอมพิวเตอร์อื่น ๆ ที่เกี่ยวข้องมาก) ที่สามารถทำการอ้างสิทธิ์เช่นนั้นได้

จากนั้นเขาก็เข้าสู่ "รูปแบบที่เปลี่ยนแปลงตลอดเวลา" อีกครั้งคะแนนส่วนใหญ่ของเขาค่อนข้างใกล้เคียงกับเรื่องไร้สาระ เขาพยายามที่จะระบุfor (int i=0; i<n;i++)ว่า "เก่าแก่และถูกจับ" และfor (int i(0); i!=n;++i)"ร้อนแรงใหม่" ความจริงก็คือในขณะที่มีหลายประเภทที่การเปลี่ยนแปลงดังกล่าวสามารถทำให้รู้สึกสำหรับintมันไม่แตกต่าง - และแม้ว่าคุณจะได้รับบางสิ่งบางอย่างก็ไม่ค่อยจำเป็นสำหรับการเขียนรหัสที่ดีหรือถูกต้อง แม้แต่ที่ดีที่สุดเขายังสร้างภูเขาจากเนินเขา

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

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

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

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

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


1

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

ความนิยมบางอย่างที่ฉันเคยเห็นมักจะเกิดจากการจัดสรรหน่วยความจำเวทมนต์ของ STL (ใช่คุณสามารถเปลี่ยนตัวจัดสรรได้ แต่ใครจะทำเช่นนั้นเมื่อเขาเริ่มด้วย C ++?) คุณมักจะได้ยินข้อโต้แย้งโดยผู้เชี่ยวชาญ C ++ ที่เวกเตอร์และอาร์เรย์มีประสิทธิภาพการทำงานที่คล้ายกันเนื่องจากเวกเตอร์ใช้อาร์เรย์ภายในและนามธรรมมีประสิทธิภาพสูงสุด ฉันพบว่าสิ่งนี้เป็นจริงในทางปฏิบัติสำหรับการเข้าถึงเวกเตอร์และแก้ไขค่าที่มีอยู่ แต่ไม่จริงสำหรับการเพิ่มรายการใหม่การก่อสร้างและการทำลายเวกเตอร์ gprof แสดงให้เห็นว่าสะสมเวลา 25% สำหรับการสมัครใช้งานในตัวสร้างเวกเตอร์, destructors, memmove (สำหรับการย้ายทั้งเวกเตอร์เพื่อเพิ่มองค์ประกอบใหม่) และตัวดำเนินการเวกเตอร์ที่โอเวอร์โหลดอื่น ๆ (เช่น ++)

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


0

แม้ว่าฉันจะชอบ Linus Thorvalds แต่การพูดจาโผงผางเป็นเรื่องไร้สาระ

หากคุณต้องการที่จะเห็นการพูดจาโผงผางที่สำคัญนี่คือหนึ่ง: "ทำไม C ++ ไม่ดีต่อสิ่งแวดล้อมทำให้โลกร้อนและฆ่าลูกสุนัข" http://chaosradio.ccc.de/camp2007_m4v_1951.htmlเนื้อหาเพิ่มเติม: http: // www .fefe.de / C ++ /

การสนทนาที่สนุกสนาน Imho


0

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

- รับค่าอินเทอร์เฟซที่ไม่ใช้งาน

- แสดงการรวมมากกว่าการสืบทอด

- แสดงที่ฟังก์ชันฟรีที่เป็นไปได้กับวิธีการของสมาชิก

- ใช้รหัส RAII เสมอเพื่อให้รหัสของคุณปลอดภัยอย่างยิ่ง หลีกเลี่ยงการลองจับ

ใช้งานตัวชี้สมาร์ทหลีกเลี่ยงตัวชี้เปล่า

- ค่าความหมายที่ดีกว่าเพื่ออ้างอิงความหมาย

- อย่าประดิษฐ์ล้อใหม่ใช้ stl และเพิ่ม

- ใช้สำนวน Pimpl เพื่อซ่อนความเป็นส่วนตัวและ / หรือเพื่อให้คอมไพเลอร์ไฟร์วอลล์


-6

ไม่ทำการวางขั้นสุดท้าย;เมื่อสิ้นสุดการประกาศ clase อย่างน้อยใน VC บางเวอร์ชัน


4
นี่อาจเป็นความผิดพลาดทั่วไปสำหรับผู้เริ่มต้น (เกือบทุกอย่างสำหรับใครบางคนที่ยังเรียนรู้ไวยากรณ์พื้นฐาน) แต่มีหลายคนที่เรียกตัวเองว่ามีความสามารถและยังคงพบข้อผิดพลาดนี้ที่น่าสังเกต?
Fred Nurk

1
เขียนมันเพียงเพราะคอมไพเลอร์ให้คุณมีข้อผิดพลาดที่ไม่เกี่ยวข้องกับการขาดเครื่องหมายอัฒภาค
Marco Mustapic

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