โปรแกรม C ++ ควรจับข้อยกเว้นทั้งหมดและป้องกันข้อยกเว้นจากการเดือดปุด ๆ ในอดีต main () หรือไม่?


29

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

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

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

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

ข้อได้เปรียบเล็ก ๆ น้อย ๆ ที่ฉันคิดได้ในการยอมให้มีข้อยกเว้นเกิดฟองขึ้นในอดีตmain()คือทำให้เกิดผลลัพธ์std::exception::what()ที่จะพิมพ์ไปยังเทอร์มินัล (อย่างน้อยก็ด้วยโปรแกรมที่คอมไพล์ด้วย gcc บน Linux) บนมืออื่น ๆ นี้เป็นที่น่ารำคาญที่จะบรรลุโดยแทนที่จะจับข้อยกเว้นทั้งหมดมาจากstd::exceptionและการพิมพ์ผลมาจากstd::exception::what()และถ้ามันเป็นที่พึงปรารถนาที่จะพิมพ์ข้อความจากข้อยกเว้นที่ไม่ได้มาจากstd::exceptionนั้นก็จะต้องถูกจับก่อนที่จะออกmain()เพื่อพิมพ์ ข้อความ.

ข้อเสียเปรียบเล็กน้อยที่ฉันสามารถคิดได้ในการอนุญาตให้มีการยกเว้นเกิดฟองขึ้นในอดีตmain()คือการทิ้งขยะที่ไม่ต้องการ สำหรับกระบวนการที่ใช้หน่วยความจำจำนวนมากสิ่งนี้อาจสร้างความรำคาญและควบคุมพฤติกรรมการถ่ายโอนข้อมูลหลักจากโปรแกรมที่ต้องการการเรียกใช้ฟังก์ชันเฉพาะระบบปฏิบัติการ ในทางตรงกันข้ามถ้าถ่ายโอนข้อมูลหลักและออกเป็นที่ต้องการแล้วนี้อาจแทนจะประสบความสำเร็จในเวลาใด ๆ โดยการโทรและการออกโดยไม่ต้องถ่ายโอนข้อมูลหลักสามารถทำได้ในเวลาใดก็ได้โดยการเรียกstd::abort()std::exit()

โดยทั่วไปฉันไม่คิดว่าฉันเคยเห็นwhat(): ...ข้อความเริ่มต้นที่พิมพ์โดยโปรแกรมที่มีการกระจายอย่างกว้างขวางเมื่อระบบขัดข้อง

อะไรคือข้อโต้แย้งที่แข็งแกร่งสำหรับหรือต่อต้านการอนุญาตให้มีการยกเว้น C ++ เพื่อทำให้เกิดฟองที่ผ่านมาmain()?

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




@gnat คำถามของฉันเฉพาะเจาะจงมากขึ้น มันเกี่ยวกับข้อยกเว้น C ++ ที่ไม่สามารถจัดการได้มากกว่าการจัดการข้อยกเว้นในภาษาการเขียนโปรแกรมใด ๆ ภายใต้สถานการณ์ใด ๆ
Praxeolitic

สำหรับโปรแกรมแบบมัลติเธรดสิ่งต่าง ๆ อาจมีความซับซ้อนหรือแปลกใหม่มากขึ้น (ข้อยกเว้น wrt)
Basile Starynkevitch

1
พวกซอฟต์แวร์สำหรับAriane 5หวังว่าพวกเขาจะได้รับข้อยกเว้นทั้งหมดในช่วงเปิดตัวครั้งแรก ...
Eric Towers

คำตอบ:


25

ปัญหาหนึ่งที่มีการให้ข้อยกเว้นไปที่ผ่านมาที่สำคัญคือโปรแกรมจะจบลงด้วยการโทรไปที่พฤติกรรมเริ่มต้นคือการโทรstd::terminate std::abortเป็นการนำไปใช้งานที่กำหนดไว้เท่านั้นหากการคลายสแต็กเสร็จสิ้นก่อนการโทรterminateเพื่อให้โปรแกรมของคุณสามารถจบได้โดยไม่ต้องเรียก destructor เดียว! หากคุณมีทรัพยากรบางอย่างที่จำเป็นต้องได้รับการเรียกคืนโดยการโทรแบบ destructor จริงๆคุณกำลังตกอยู่ในอันตราย ...


12
หากคุณมีทรัพยากรบางอย่างที่จำเป็นต้องได้รับการกู้คืนโดยการโทรแบบ destructor คุณจะถูกดอง ... => ระบุว่า (โชคไม่ดี) C ++ ค่อนข้างจะผิดพลาดได้ง่าย โปรแกรมของคุณได้ตลอดเวลา (เช่นนักฆ่า OOM ใน Unix) โปรแกรมของคุณ (และสภาพแวดล้อม) จำเป็นต้องได้รับการปรับแต่งเพื่อรองรับข้อขัดข้อง
Matthieu M.

@MatthieuM คุณกำลังบอกว่า destructors ไม่ใช่สถานที่ที่ดีสำหรับการล้างทรัพยากรที่ควรเกิดขึ้นแม้ในระหว่างที่เกิดความผิดพลาดหรือไม่?
Praxeolitic

6
@Praxeolitic: ฉันกำลังบอกว่าไม่ใช่ทุกข้อผิดพลาดที่จะคลายสแต็กเช่นstd::abortไม่เช่นนั้นการยืนยันที่ล้มเหลวก็ไม่ได้เป็นเช่นนั้น นอกจากนี้ยังควรทราบว่าในระบบปฏิบัติการสมัยใหม่ระบบปฏิบัติการจะล้างทรัพยากรจำนวนมาก (หน่วยความจำด้ามจับไฟล์ ... ) ซึ่งเชื่อมโยงกับ ID กระบวนการ ในที่สุดฉันก็พูดเป็นนัยที่ "การป้องกันในเชิงลึก": มันไม่น่าเชื่อถือที่จะคาดหวังว่ากระบวนการอื่น ๆ ทั้งหมดจะพิสูจน์ข้อผิดพลาดและจะปล่อยทรัพยากรที่พวกเขาได้รับเสมอ (ช่วงการเขียนแฟ้มเสร็จ ... ) มัน ...
Matthieu M.

3
@Praxeolitic ไม่ซอฟต์แวร์ของคุณต้องไม่ให้ข้อมูลเสียหายระหว่างไฟดับ และหากคุณสามารถจัดการสิ่งนั้นได้คุณสามารถจัดการข้อมูลที่ไม่เสียหายหลังจากข้อยกเว้นที่ไม่สามารถจัดการได้
253751

1
@Praxeolitic: จริงๆแล้วมันมีอยู่จริง คุณจะต้องการฟังWM_POWERBROADCASTข้อความ ใช้งานได้เฉพาะเมื่อคอมพิวเตอร์ของคุณใช้พลังงานจากแบตเตอรี่ (หากคุณใช้แล็ปท็อปหรือ UPS)
Brian

28

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

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

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


คุณแน่ใจหรือว่าพฤติกรรมเริ่มต้นของข้อยกเว้น C ++ ที่ทิ้งไว้main() มีส่วนเกี่ยวข้องกับระบบปฏิบัติการหรือไม่? ระบบปฏิบัติการอาจไม่รู้จัก C ++ ฉันเดาว่ามันถูกกำหนดโดยโค้ดที่คอมไพเลอร์แทรกอยู่ในโปรแกรม
Praxeolitic

5
@Praxeolitic: มันเป็นมากกว่าที่ระบบปฏิบัติการตั้งค่าการประชุมและคอมไพเลอร์สร้างรหัสเพื่อตอบสนองการประชุมเหล่านั้นเมื่อโปรแกรมยุติโดยไม่คาดคิด บรรทัดล่างคือการยกเลิกโปรแกรมที่ไม่คาดคิดควรหลีกเลี่ยงเมื่อเป็นไปได้อย่างสมเหตุสมผล
Bart van Ingen Schenau

คุณสูญเสียการควบคุมภายในขอบเขตของ std C ++ เท่านั้น วิธีการใช้งานที่เฉพาะเจาะจงในการจัดการ "การขัดข้อง" นั้นควรจะมีอยู่ C ++ มักจะไม่ทำงานในสุญญากาศ
Martin Ba

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

11

TL; DR : สเปคพูดว่าอย่างไร?


อ้อมทางเทคนิค ...

เมื่อมีข้อผิดพลาดเกิดขึ้นและไม่มีตัวจัดการพร้อมใช้งาน:

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

หลังจะมีประโยชน์สำหรับข้อบกพร่องไม่บ่อยนัก (เพราะการทำซ้ำพวกเขาเป็นกระบวนการที่เสียเวลา)


ไม่ว่าจะจับข้อยกเว้นทั้งหมดหรือไม่เป็นที่สุดเรื่องของสเปค:

  • ระบุวิธีการรายงานข้อผิดพลาดของผู้ใช้หรือไม่ (ค่าไม่ถูกต้อง, ...)
  • มันระบุวิธีการรายงานข้อผิดพลาดการทำงานหรือไม่ (ไม่มีไดเรกทอรีเป้าหมาย, ... )
  • ระบุวิธีการรายงานข้อผิดพลาดทางเทคนิคหรือไม่ (ยืนยันการยิง, ... )

สำหรับโปรแกรมการผลิตใด ๆ ควรระบุสิ่งนี้และคุณควรปฏิบัติตามข้อกำหนด (และอาจโต้แย้งว่ามีการเปลี่ยนแปลง)

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


1
นี้. ปัจจัยการตัดสินใจ โดยทั่วไปอยู่นอกขอบเขตของ C ++
Martin Ba

4

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

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

ดังนั้นจึงเป็นการแลกเปลี่ยน


4

ทันทีที่คุณรู้ว่าคุณต้องยกเลิกไปข้างหน้าstd::terminateแล้วโทรหาคุณเพื่อกำจัดความเสียหายที่เพิ่มขึ้น

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

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

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


1

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

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

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

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

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ Local Crash Dumps ได้ที่นี่หากคุณสนใจ: รวบรวมการถ่ายโอนข้อมูลโหมดผู้ใช้


-6

ในที่สุดหากมีข้อยกเว้นทำให้ฟองหลักที่ผ่านมา () มันจะเกิดความผิดพลาดใบสมัครของคุณและในใจของฉันแอพไม่ควรผิดพลาด หากตกลงที่แอปในที่เดียวแล้วทำไมไม่ไปไหนล่ะ ทำไมต้องจัดการกับข้อยกเว้นทั้งหมด? (Sarcasm ไม่ได้แนะนำสิ่งนี้จริงๆ ... )

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

การชนเป็นชั่วโมงสมัครเล่นอย่างเคร่งครัด


8
ดังนั้นสมมติว่าทุกอย่างทำงานถูกต้องดีกว่าและเขียนทับที่เก็บข้อมูลถาวรทั้งหมดด้วยคำพูดไร้สาระแทนหรือไม่
Deduplicator

1
@Dupuplicator: ไม่: คุณสามารถรับข้อยกเว้นและจัดการได้เสมอ
Giorgio

5
mainถ้าคุณรู้วิธีจัดการกับมันคุณอาจจะจัดการกับมันนานก่อนที่คุณจะได้รับถึง หากคุณไม่ทราบวิธีจัดการกับมันการแสร้งทำคุณเห็นได้ชัดว่าเป็นข้อบกพร่องที่น่ากลัวจริงๆ
Deduplicator

8
แต่การหยุดทำงานนั้นไม่เป็นมืออาชีพอย่างสมบูรณ์และไม่สามารถยอมรับได้ => การใช้เวลาในการทำงานกับคุณสมบัติที่ไร้ประโยชน์นั้นเป็นเรื่องที่ไม่เป็นมืออาชีพ: การขัดข้องจะเกิดขึ้นเฉพาะกับผู้ใช้ปลายทางเท่านั้น ประหยัดกว่า
Matthieu M.

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