Bagaimana untuk berurusan dengan bad_alloc di C++?

Ada sebuah metode yang disebut foo yang kadang-kadang menghasilkan galat berikut:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abort

Apakah ada cara bahwa saya dapat menggunakan coba-coba-menangkap blok untuk menghentikan kesalahan ini mengakhiri program saya (semua saya ingin lakukan adalah kembali -1)?

Jika demikian, apa yang merupakan sintaks untuk itu?

Bagaimana lagi yang bisa saya berurusan dengan bad_alloc di C++?

Mengomentari pertanyaan (4)

Secara umum anda tidak, dan tidak harus mencoba, untuk menanggapi kesalahan ini. bad_alloc menunjukkan bahwa sumber daya yang tidak dapat dialokasikan karena tidak tersedia cukup memori. Dalam kebanyakan skenario program anda tidak bisa berharap untuk mengatasi dengan itu, dan mengakhiri segera adalah satu-satunya yang berarti perilaku.

Lebih buruk lagi, sistem operasi modern sering di-alokasikan: pada sistem seperti ini, malloc dan baru dapat berlaku pointer bahkan jika tidak ada cukup memori yang tersisa – std::bad_alloc tidak akan pernah dilempar, atau setidaknya bukan tanda yang dapat diandalkan dari memori kelelahan. Sebaliknya, upaya untuk akses memori yang dialokasikan maka akan mengakibatkan kesalahan segmentasi, yang tidak catchable (anda dapat handle segmentasi sinyal kesalahan, tetapi anda tidak dapat melanjutkan program sesudahnya).

Satu-satunya hal yang anda bisa lakukan ketika catching std::bad_alloc adalah mungkin log kesalahan, dan mencoba untuk memastikan aman program penghentian dengan membebaskan sumber daya yang luar biasa (tapi ini dilakukan secara otomatis dalam kegiatan normal dari tumpukan unwinding setelah kesalahan akan dilemparkan jika program menggunakan RAII tepat).

Dalam kasus-kasus tertentu program dapat mencoba untuk membebaskan beberapa memori dan coba lagi, atau menggunakan memori sekunder (= disk), bukan dari RAM tetapi peluang tersebut hanya ada dalam skenario yang sangat spesifik dengan kondisi yang ketat:

  1. Aplikasi harus memastikan bahwa hal itu berjalan pada sebuah sistem yang tidak overcommit memori, yaitu sinyal kegagalan pada alokasi daripada kemudian.
  2. Aplikasi harus mampu membebaskan memori segera, tanpa lebih lanjut disengaja alokasi sementara.

Ini sangat jarang bahwa aplikasi memiliki kontrol atas point 1 — login sistem aplikasi tidak pernah melakukan, itu adalah sistem pengaturan yang memerlukan izin root untuk mengubah.1

OK, jadi mari kita asumsikan anda sudah fixed point 1. Apa yang sekarang dapat anda lakukan adalah misalnya menggunakan LRU cache untuk beberapa data anda (mungkin beberapa sangat besar bisnis benda-benda yang dapat diregenerasi atau reloaded on demand). Selanjutnya, anda perlu untuk menempatkan sebenarnya logika yang mungkin gagal menjadi fungsi yang mendukung retry — dengan kata lain, jika itu akan dibatalkan, anda hanya dapat peluncuran ini:

`` lru_cache widget_cache;

double perform_operation(int widget_id) { std::opsional maybe_widget = widget_cache.find_by_id(widget_id); jika (tidak maybe_widget) { maybe_widget = widget_cache.toko(widget_id, load_widget_from_disk(widget_id)); } kembali maybe_widget->frobnicate(); }

...

for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) { try { kembali perform_operation(widget_id); } catch (std::bad_alloc const&) { jika (widget_cache.kosong()) throw; // error di tempat lain. widget_cache.remove_oldest(); } }

// Menangani terlalu banyak usaha yang gagal di sini. ``

Namun bahkan di sini, menggunakan std::set_new_handler bukan penanganan std::bad_alloc memberikan manfaat yang sama dan akan menjadi jauh lebih sederhana.


1 Jika anda membuat sebuah aplikasi yang tidak control point 1, dan anda sedang membaca jawaban ini, silahkan menembak saya email, saya benar-benar ingin tahu tentang keadaan anda.

Komentar (26)

Apa itu C++ Standar yang ditentukan perilaku baru di c++?

Gagasan biasa adalah bahwa jika baru operator tidak dapat mengalokasikan memori dinamis dari ukuran yang diminta, maka harus melempar pengecualian dari jenis std::bad_alloc. Namun, sesuatu terjadi bahkan sebelum bad_alloc pengecualian dilemparkan:

C++03 Bagian 3.7.4.1.3: kata

Sebuah fungsi alokasi yang gagal untuk mengalokasikan penyimpanan yang bisa memohon saat ini diinstal new_handler(18.4.2.2), jika ada. [Catatan: program yang disediakan alokasi fungsi dapat mendapatkan alamat saat ini diinstal new_handler menggunakan set_new_handler fungsi (18.4.2.3).] Jika alokasi fungsi dinyatakan dengan kosong pengecualian-spesifikasi (15.4), membuang(), gagal untuk mengalokasikan penyimpanan, itu akan mengembalikan suatu null pointer. Lainnya fungsi alokasi yang gagal untuk mengalokasikan penyimpanan hanya akan menunjukkan kegagalan membuang-ing pengecualian dari kelas std::bad_alloc (18.4.2.1) atau kelas yang berasal dari std::bad_alloc.

Perhatikan contoh kode berikut:


#include 
#include 

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr 
Komentar (2)
Larutan

Anda dapat menangkap hal itu seperti yang lain kecuali:

try {
  foo();
}
catch (const std::bad_alloc&) {
  return -1;
}

Cukup apa yang anda dapat berguna lakukan dari titik ini adalah terserah anda, tapi itu's pasti layak secara teknis.

Komentar (0)

Saya tidak akan menyarankan hal ini, karena bad_alloc berarti anda memori. Itu akan menjadi yang terbaik untuk menyerah, bukannya mencoba untuk pulih. Namun berikut ini adalah solusi yang anda minta:

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}
Komentar (5)

Boleh saya sarankan lebih sederhana (dan lebih cepat) solusi untuk ini. baru operator akan mengembalikan null jika memori tidak dapat dialokasikan.

int fv() {
    T* p = new (std::nothrow) T[1000000];
    if (!p) return -1;
    do_something(p);
    delete p;
    return 0;
}

Saya berharap ini bisa membantu!

Komentar (0)

Biarkan anda foo program exit dengan cara yang terkontrol:

#include      /* exit, EXIT_FAILURE */

try {
    foo();
} catch (const std::bad_alloc&) {
    exit(EXIT_FAILURE);
}

Kemudian menulis program shell yang memanggil program yang sebenarnya. Karena ruang alamat yang terpisah, keadaan program shell selalu didefinisikan dengan baik.

Komentar (0)