Hentikan eksekusi tanpa melewatkan destruktor

Apakah mungkin menghentikan eksekusi perangkat lunak tanpa melewatkan panggilan ke destruktor? Misalnya, pada kode di bawah, destruktor untuk test tidak akan pernah dipanggil karena pernyataan 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;
}

Yang saya perlukan adalah cara untuk mengakhiri program (dari dalam func()) yang memanggil semua destruktor yang diperlukan sebelum dihentikan. Sejauh ini saya telah menangani ini melalui nilai pengembalian func(), seperti pada:

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

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

Masalah dengan metode ini adalah metode ini dengan cepat menjadi sangat mengganggu (dan kode membengkak) untuk dikelola begitu Anda perlu menerapkannya ke serangkaian fungsi yang disarangkan.

Apakah ada cara untuk mencapai hasil yang sama dari contoh kedua (panggilan destruktor yang tepat) dengan sintaksis yang mirip dengan contoh pertama (panggil exit(1) di mana pun Anda berada)?


person Malabarba    schedule 29.11.2011    source sumber
comment
sudahkah Anda mempertimbangkan untuk menggunakan pengecualian? jika Anda memberikan pengecualian pada fungsi tersebut, destruktor akan dipanggil.   -  person Nerdtron    schedule 29.11.2011
comment
Jangan gunakan exit; sebaliknya, berikan pengecualian dan tangkap di level main.   -  person Kerrek SB    schedule 29.11.2011
comment
Ini adalah tujuan utama pengecualian (untuk memungkinkan eksekusi destruktor).   -  person Steven Lu    schedule 29.11.2011
comment
@Nerdtron: Sedikit koreksi: jika Anda melempar dan menangkap pengecualian, destruktor akan dipanggil. Kalau tidak tertangkap, maka tidak ditentukan apakah mereka dipanggil.   -  person Mike Seymour    schedule 29.11.2011


Jawaban (4)


Lemparkan pengecualian, tangkap di main dan kembalikan.

Ini tidak bergantung pada apa pun yang menangkap pengecualian Anda tanpa membuangnya kembali.

person JoeG    schedule 29.11.2011
comment
Catatan tentang orang lain yang mengetahui pengecualian Anda adalah catatan yang penting. Jika Anda ingin bersikap keras, Anda dapat melemparkan beberapa jenis selain pengecualian, sesuatu yang tidak mungkin ditangani oleh kode lain. Adakah yang tahu kalau Anda bisa melempar nullptr? - person Michael Price; 29.11.2011
comment
Oh, dan tentu saja, Anda tidak dapat menghindari sintaks catch(...) yang ditakuti. - person Michael Price; 29.11.2011
comment
@Michael Harga: Anda bisa membuat tipe Anda sendiri dan membuangnya. catch(...) masih bisa menjadi masalah. Mudah-mudahan kode apa pun yang melakukan hal itu hanya mencatat dan menampilkan kembali pengecualian yang sama. - person Fred Larson; 29.11.2011
comment
@Michael: taruhan terbaik adalah dengan mendefinisikan kelas, sebut saja systemexit, yang mengambil kode keluar yang diinginkan sebagai parameter konstruktor. Lemparkan itu. Jika tidak memiliki std::exception sebagai kelas dasar, maka satu-satunya cara siapa pun dapat menangkapnya adalah: (a) menangkap secara eksplisit systemexit, dalam hal ini mereka tahu apa artinya jadi semoga saja mereka melakukannya untuk alasan yang baik dan akan membuangnya kembali; (b) catch(...), dalam hal ini mari kita berharap mereka melakukannya untuk alasan yang baik dan akan mengulang kembali atau paling buruk menghentikan. Risiko lainnya adalah seseorang memanggil func dari fungsi noexcept(true): hasilnya adalah penghentian. - person Steve Jessop; 29.11.2011
comment
@SteveJessop - Lebih baik lagi karena Anda kemudian dapat membebani operator () pada systemexit dan di blok catch Anda dapat mengatakan myexit(); (di mana myexit adalah nama objek yang Anda tangkap). Anda dapat menggunakan ini untuk memanggil destruktor untuk global apa pun yang tidak akan dipanggil karena tidak disentuh oleh pelepasan tumpukan. - person Michael Price; 29.11.2011
comment
@Michael Price: Tapi main() bisa kembali, yang akan menghancurkan global mana pun. Jika ada pointer global (bergidik), orang akan berharap ada kode di akhir main() untuk mengatasinya. - person Fred Larson; 29.11.2011
comment
Apakah global dibersihkan saat Anda menelepon exit(). Saya rasa masuk akal jika mereka harus melakukannya. - person Michael Price; 29.11.2011
comment
Saya sebenarnya menguji ini sebelum bertanya dan tidak berhasil. Ternyata salah satu petunjuk saya menyebabkan perilaku tidak ditentukan :-$. - person Malabarba; 29.11.2011

Anda dapat mengandalkan pelepasan tumpukan untuk ini : ketika Anda ingin keluar, berikan pengecualian dan tangkap di main().

person NPE    schedule 29.11.2011

Ada beberapa cara untuk melakukannya dengan bersih.

Salah satu solusinya adalah dengan menggunakan fungsi atexit, yang hanya memanggil penunjuk fungsi yang diberikan ketika program dihentikan.

Anda harus mengalokasikan semua objek Anda dari heap, memelihara beberapa tabel global dengan pointer ke semua instance kelas yang dipakai, dan kemudian melakukan iterasi melalui tabel delete untuk setiap instance dalam fungsi yang terdaftar.

person Max DeLiso    schedule 29.11.2011

person    schedule
comment
Seperti yang ditunjukkan dalam komentar pada jawaban lain, global akan dibersihkan hanya dengan kembali dari main atau dengan memanggil exit, jadi saya tidak yakin apa yang akan didapat oleh operator saya yang kelebihan beban pada akhirnya. Mungkin saya akan memikirkan kegunaan lain untuk itu. - person Michael Price; 29.11.2011