หยุดการดำเนินการโดยไม่ข้ามตัวทำลาย

เป็นไปได้ไหมที่จะยุติการทำงานของซอฟต์แวร์โดยไม่ข้ามการเรียกไปยังตัวทำลาย? ตัวอย่างเช่น ในโค้ดด้านล่าง destructor สำหรับ test จะไม่ถูกเรียกเนื่องจากคำสั่ง exit(1)

#include <iostream>
#include <cstdlib>
using namespace std;

class A{
public:
    A(){cout << "Constructed.\n";}
    ~A(){cout << "Destroyed.\n";}
};

void func()
{
    //Assuming something went wrong:
    exit(1);  
}

int main(int argc, char *argv[])
{
    A test;
    func();
    return 0;
}

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

bool func()
{
    //Assuming something went wrong:
    return false;
}

int main(int argc, char *argv[])
{
    A test;
    if( !func() )return 1;
    return 0;
}

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

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


person Malabarba    schedule 29.11.2011    source แหล่งที่มา
comment
คุณเคยพิจารณาใช้ข้อยกเว้นหรือไม่? หากคุณส่งข้อยกเว้นในฟังก์ชัน destructors จะถูกเรียก   -  person Nerdtron    schedule 29.11.2011
comment
อย่าใช้ exit; แทนที่จะส่งข้อยกเว้นและจับมันที่ระดับ main   -  person Kerrek SB    schedule 29.11.2011
comment
นี่คือวัตถุประสงค์หลักของข้อยกเว้น (เพื่ออนุญาตให้มีการเรียกใช้ตัวทำลาย)   -  person Steven Lu    schedule 29.11.2011
comment
@Nerdtron: การแก้ไขเล็กน้อย: หากคุณส่ง และจับ ข้อยกเว้น destructors จะถูกเรียก ถ้าไม่จับก็ไม่ระบุว่าจะถูกเรียกหรือไม่   -  person Mike Seymour    schedule 29.11.2011


คำตอบ (4)


โยนข้อยกเว้น จับมันใน main แล้วส่งคืน

สิ่งนี้ไม่ต้องอาศัยสิ่งอื่นใดที่ตรงกับข้อยกเว้นของคุณโดยไม่ต้องโยนมันใหม่

person JoeG    schedule 29.11.2011
comment
หมายเหตุเกี่ยวกับผู้อื่นที่ตรงตามข้อยกเว้นของคุณเป็นสิ่งสำคัญ หากคุณต้องการใช้งานหนัก คุณสามารถโยนประเภทอื่นนอกเหนือจากข้อยกเว้น ซึ่งเป็นสิ่งที่ไม่น่าจะได้รับการจัดการโดยโค้ดอื่น ไม่มีใครรู้ว่าคุณสามารถโยน nullptr ได้หรือไม่? - person Michael Price; 29.11.2011
comment
โอ้ และแน่นอน คุณไม่สามารถหลีกเลี่ยงไวยากรณ์ catch(...) อันน่าหวาดกลัวได้ - person Michael Price; 29.11.2011
comment
@Michael Price: คุณสามารถสร้างประเภทของคุณเองแล้วโยนสิ่งนั้นได้ catch(...)ยังคงอาจเป็นปัญหาได้ หวังว่าโค้ดใด ๆ ที่ทำอย่างนั้นจะบันทึกและโยนข้อยกเว้นเดิมอีกครั้ง - person Fred Larson; 29.11.2011
comment
@Michael: ทางออกที่ดีที่สุดคือเพียงกำหนดคลาสเรียกมันว่า systemexit ซึ่งรับโค้ดทางออกที่ต้องการเป็นพารามิเตอร์ตัวสร้าง โยนอันนั้น ถ้ามันไม่มี std::exception เป็นคลาสพื้นฐาน ดังนั้นวิธีเดียวที่ทุกคนจะสามารถจับมันได้คือ: (a) จับมันอย่างชัดเจน systemexit ซึ่งในกรณีนี้พวกเขารู้ว่ามันหมายถึงอะไร ดังนั้นหวังว่าพวกเขาจะทำมันด้วยเหตุผลที่ดีและ จะโยนมันขึ้นมาใหม่ (b) catch(...) ในกรณีนี้ เราหวังว่าพวกเขากำลังทำด้วยเหตุผลที่ดีและจะโยนใหม่หรือยุติอย่างเลวร้ายที่สุด ความเสี่ยงอีกประการหนึ่งคือมีคนเรียก func จากฟังก์ชัน noexcept(true): ผลลัพธ์คือการยุติ - person Steve Jessop; 29.11.2011
comment
@SteveJessop - ดียิ่งขึ้นเนื่องจากคุณสามารถโอเวอร์โหลดตัวดำเนินการ () บน systemexit และในบล็อก catch ของคุณคุณสามารถพูด myexit(); (โดยที่ myexit เป็นชื่อของวัตถุที่คุณจับได้) คุณสามารถใช้สิ่งนี้เพื่อเรียกตัวทำลายล้างสำหรับโกลบอลใด ๆ ที่จะไม่ถูกเรียกเนื่องจากพวกมันไม่ได้สัมผัสกับสแต็กคลี่คลาย - person Michael Price; 29.11.2011
comment
@Michael Price: แต่ main() สามารถกลับมาได้ซึ่งจะทำลายโกลบอลใด ๆ หากมีตัวชี้ทั่วโลก (ตัวสั่น) เราคงหวังว่าจะมีโค้ดที่ส่วนท้ายของ main() เพื่อจัดการกับสิ่งนั้นต่อไป - person Fred Larson; 29.11.2011
comment
Globals ได้รับการทำความสะอาดเมื่อคุณโทร exit() ฉันเดาว่ามันสมเหตุสมผลที่พวกเขาควรจะ - person Michael Price; 29.11.2011
comment
ฉันทดสอบสิ่งนี้จริง ๆ ก่อนถามและมันก็ไม่ได้ผล ปรากฎว่าตัวชี้ตัวหนึ่งของฉันทำให้เกิดพฤติกรรมที่ไม่ระบุรายละเอียด :-$ - person Malabarba; 29.11.2011

คุณสามารถพึ่งพา stack unwinding สำหรับสิ่งนี้ : เมื่อคุณต้องการออก ให้โยนข้อยกเว้นแล้วจับใน main()

person NPE    schedule 29.11.2011

มีหลายวิธีในการทำเช่นนี้อย่างหมดจด

วิธีแก้ไขประการหนึ่งคือการใช้ฟังก์ชัน atexit ซึ่งจะเรียกใช้ตัวชี้ฟังก์ชันที่กำหนดเมื่อโปรแกรมหยุดทำงาน

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

person Max DeLiso    schedule 29.11.2011

person    schedule
comment
ตามที่ระบุไว้ในความคิดเห็นในคำตอบอื่น globals จะได้รับการทำความสะอาดเพียงแค่กลับจาก main หรือโดยการเรียก exit ดังนั้นฉันไม่แน่ใจว่าผู้ให้บริการของฉันทำงานหนักเกินไปจะทำให้คุณได้รับอะไรในที่สุด บางทีฉันอาจจะคิดถึงการใช้งานอื่นสำหรับมัน - person Michael Price; 29.11.2011