ยกเลิกยุติหรือออก?


112

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


3
นี่ไม่ใช่รายการที่ซ้ำกัน แต่เป็นชุดย่อยที่มีคำตอบที่ดี stackoverflow.com/questions/397075/…และมันถูกแท็ก C ++ ด้วย!
Ellie Kesselman

std::abortมีความสมเหตุสมผลหากไม่สามารถแก้ไขข้อยกเว้นในตัวทำลายได้
Daniel

1
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับstd::terminateดูบทความเหล่านี้ในบล็อก C ++ ที่ยอดเยี่ยมของ Andrzej: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Ohad Schneider

คำตอบ:


3

คำแนะนำของฉันคือไม่ใช้สิ่งเหล่านี้ แต่catchมีข้อยกเว้นที่คุณไม่สามารถจัดการในmain()และก็returnจากที่นั่น ซึ่งหมายความว่าคุณได้รับการรับรองว่าการคลายสแต็กเกิดขึ้นอย่างถูกต้องและมีการเรียกตัวทำลายทั้งหมด กล่าวอีกนัยหนึ่ง:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}

8
@Neil: ตกลงกันโดยทั่วไป แต่ข้อยกเว้นที่โปรแกรมไม่สามารถจัดการได้ควรรายงานและเปลี่ยนใหม่ ปล่อยให้แอพพัง
John Dibling

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

5
ข้อยกเว้นที่ไม่พบแม้แต่ตัวจัดการระดับบนสุดอาจเรียกใช้สิ่งอำนวยความสะดวกการรายงานระบบที่ทิ้งกระบวนการและอัปโหลดรายงานข้อยกเว้นเพื่อให้ผู้พัฒนาสนใจเช่น Windows Error Reporting, รายงานข้อผิดพลาด Mac OS X และบันทึกข้อผิดพลาดของแอปพลิเคชัน iPhone
JBRWilkinson

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

11
-1 เพราะนี่ไม่ได้ตอบคำถามครึ่งหนึ่ง "อะไรคือความแตกต่างระหว่าง [ยกเลิกยุติหรือออก]" คำตอบนี้ดีกว่า: stackoverflow.com/a/397081/353094นอกจากนี้stackoverflow.com/a/2820407/353094ก็เป็นคำตอบที่ดี
leetNightshade

149
  • การยกเลิกหมายถึงการสิ้นสุด "ผิดปกติ" ของโปรแกรมและเพิ่มสัญญาณ POSIX SIGABRT ซึ่งหมายความว่าตัวจัดการใด ๆ ที่คุณได้ลงทะเบียนไว้สำหรับสัญญาณนั้นจะถูกเรียกใช้แม้ว่าโปรแกรมจะยังคงยุติการใช้คำต่อท้ายไม่ว่าในกรณีใดก็ตาม โดยปกติคุณจะใช้abortในโปรแกรม C เพื่อออกจากกรณีข้อผิดพลาดที่ไม่คาดคิดซึ่งข้อผิดพลาดน่าจะเป็นจุดบกพร่องในโปรแกรมมากกว่าสิ่งที่ป้อนไม่ดีหรือเครือข่ายล้มเหลว ตัวอย่างเช่นคุณอาจabortพบว่าโครงสร้างข้อมูลมีตัวชี้ NULL อยู่ในนั้นเมื่อสิ่งนั้นไม่ควรเกิดขึ้นอย่างมีเหตุผล

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

  • std :: terminateคือสิ่งที่เรียกโดยอัตโนมัติในโปรแกรม C ++ เมื่อมีข้อยกเว้นที่ไม่สามารถจัดการได้ โดยพื้นฐานแล้วจะเทียบเท่ากับ C ++ abortโดยสมมติว่าคุณกำลังรายงานข้อผิดพลาดพิเศษทั้งหมดของคุณโดยใช้ข้อยกเว้น นี้เรียกร้องการจัดการที่ถูกกำหนดโดยมีฟังก์ชั่นซึ่งเริ่มต้นโดยเพียงแค่โทรstd::set_terminateabort

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

std::terminateควรถือเป็นเครื่องมือรายงานข้อผิดพลาดสุดท้ายแม้ใน C ++ ปัญหาstd::terminateคือตัวจัดการการยกเลิกไม่สามารถเข้าถึงข้อยกเว้นที่ไม่สามารถจัดการได้ดังนั้นจึงไม่มีทางที่จะบอกได้ว่ามันคืออะไร โดยปกติคุณจะดีกว่ามากในการห่อหลักทั้งหมดในtry { } catch (std::exception& ex) { }บล็อก อย่างน้อยคุณก็สามารถรายงานข้อมูลเพิ่มเติมเกี่ยวกับข้อยกเว้นที่มาจากstd::exception(แม้ว่าแน่นอนว่าข้อยกเว้นที่ไม่ได้มาจากstd::exceptionจะยังคงไม่สามารถจัดการได้)

การห่อเนื้อหาmainในtry { } catch(...) { }นั้นไม่ได้ดีไปกว่าการตั้งค่าตัวจัดการการยกเลิกเนื่องจากคุณไม่มีสิทธิ์เข้าถึงข้อยกเว้นที่เป็นปัญหาอีกต่อไป แก้ไข:ตามคำตอบของนีลบัตเตอร์เวิร์ ธ มีประโยชน์ที่สแต็กจะคลายออกในกรณีนี้ซึ่ง (ค่อนข้างน่าแปลกใจ) ไม่เป็นความจริงสำหรับข้อยกเว้นที่ไม่สามารถจัดการได้


10
คุณสามารถอัปเดตคำตอบนี้ด้วยข้อมูล C ++ 11 ได้หรือไม่ ดูเหมือนว่าตอนนี้มีหลายวิธีในการรับข้อยกเว้นในการจับ (... ) และในตัวจัดการการยุติ
อ้าง

1
ใน C ++ ยุติจัดการไม่std::current_exception()ได้มีการเข้าถึงข้อยกเว้นผ่าน ดูตัวอย่างได้ที่นี่: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm

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

2
@seattlecpp คุณสามารถสร้างมันขึ้นมาใหม่และดูข้อมูลอ้างอิงซึ่งคุณสามารถตรวจสอบได้
gpeche

16

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

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

โดยเฉพาะอย่างยิ่งโปรดทราบว่า std :: terminate ถือเป็นตัวจัดการข้อยกเว้นในบริบทที่ std :: terminate ถูกเรียกใช้เนื่องจากมีข้อยกเว้นที่ไม่สามารถจัดการได้และคุณสามารถตรวจสอบว่าข้อยกเว้นคืออะไรและตรวจสอบได้โดยใช้ C ++ 11 โดยใช้ std :: rethrow_exception และ std :: current_exception มันเป็นทั้งหมดในการโพสต์ของฉัน


ขอแนะนำให้มีตัวจัดการการล้างข้อมูลในกรณีที่โปรแกรมหยุดทำงานเนื่องจากสัญญาณของระบบหรือไม่? ตัวอย่างเช่นการเข้าถึงหน่วยความจำที่ไม่ถูกต้องนำไปสู่การสร้างสัญญาณ SIGSEGV ในกรณีนี้จะเป็นการดีเพียงแค่ปล่อยให้โปรแกรมยุติการทำงานและมีไฟล์หลักหรือลงทะเบียนตัวจัดการสัญญาณเพื่อทำการล้างข้อมูล ?? มีข้อกังวลใด ๆ เกี่ยวกับการล้างข้อมูลในขณะที่จัดการสัญญาณระบบเมื่อเทียบกับการล้างข้อมูลในขณะที่จัดการ std :: terminate หรือไม่?
kartik trivikram

12

quick_exit () !

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

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

หมายเหตุ: MSVC ++ ยังไม่รองรับ quick_exit ()จนถึงเวอร์ชัน 2015


4
  • การยุติทำให้คุณมีความเป็นไปได้ที่จะลงทะเบียนสิ่งที่จะเกิดขึ้นเมื่อถูกเรียก ควรเป็นหนึ่งในอีกสองคน
  • exit เป็นทางออกปกติที่อนุญาตให้ระบุสถานะการออก ตัวจัดการที่ลงทะเบียนโดย at_exit () ถูกเรียกใช้
  • การทำแท้งเป็นทางออกที่ผิดปกติ สิ่งเดียวที่รันคือตัวจัดการสัญญาณสำหรับ SIGABRT

4
  • terminate () ถูกเรียกโดยอัตโนมัติเมื่อเกิดข้อยกเว้นที่ไม่สามารถจัดการได้ ตามค่าเริ่มต้นยุติ () เรียก abort () คุณสามารถตั้งค่าจุดจับแบบกำหนดเองด้วยฟังก์ชัน set_terminate ()

    abort () ส่งสัญญาณ SIGABRT

    exit () ไม่จำเป็นต้องเป็นเรื่องเลวร้าย ออกจากแอปพลิเคชันสำเร็จและเรียกใช้ฟังก์ชัน atexit () ตามลำดับ LIFO ปกติฉันไม่เห็นสิ่งนี้ในแอปพลิเคชัน C ++ อย่างไรก็ตามฉันเห็นมันในแอปพลิเคชันที่ใช้ Unix จำนวนมากซึ่งจะส่งรหัสออกในตอนท้าย โดยปกติทางออก (0) แสดงว่าแอปพลิเคชันทำงานสำเร็จ


8
ไม่สำเร็จ! ทั้งใน Unix และ DOS exit (0) หมายถึงความสำเร็จและค่าอื่น ๆ ที่ส่งไปยัง exit () หมายถึงความล้มเหลวไม่ใช่ทางอื่น!
Richard Barrell
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.