Domanda

C'è un metodo chiamato foo che a volte restituisce il seguente errore:

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

C'è un modo in cui posso utilizzare un blocco try-catch per interrompere questo errore dal termine del mio programma (tutto quello che voglio fare è ritornare -1)?

In caso affermativo, qual è la sintassi per questo?

In che altro modo posso affrontare bad_alloc in C ++?

È stato utile?

Soluzione

Puoi prenderlo come qualsiasi altra eccezione:

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

Abbastanza ciò che puoi fare da questo punto dipende da te, ma è decisamente fattibile tecnicamente.

Altri suggerimenti

in generale tu non è possibile , e non dovrebbe provare , per rispondere a questo errore. bad_alloc indica che una risorsa non può essere assegnata perché non È disponibile abbastanza memoria. Nella maggior parte degli scenari il tuo programma non può sperare di affrontarlo e la terminazione presto è l'unico comportamento significativo.

Peggio dei sistemi operativi, i moderni sistemi operativi spesso allocati: su tali sistemi, malloc e new possono un puntatore valido anche se non c'è abbastanza memoria libera a sinistra - std::bad_alloc non verrà mai lanciato, o non è almeno un segno affidabile di memoria esaurimento. Invece, tenta di Access La memoria assegnata provocherà un errore di segmentazione, che non è attibile (è possibile Maniglia Segnale di errore di segmentazione, ma non è possibile riprendere il programma in seguito ).

L'unica cosa che potresti fare quando cattura la cattura std::bad_alloc è forse registrare l'errore e cercare di garantire una risoluzione del programma sicura liberando risorse eccezionali (ma questo viene fatto automaticamente nel normale corso di schianto dopo che l'errore viene lanciato se Il programma utilizza RAII in modo appropriato).

In alcuni casi il programma può tentare di liberare qualche memoria e riprovare o utilizzare memoria secondaria (= disco) anziché RAM, ma queste opportunità esistono solo in scenari molto specifici con condizioni rigorose:

    .
  1. L'applicazione deve assicurarsi di funzionare su un sistema che non sovrasta la memoria , cioè segnala il fallimento sullo assegnazione più tardi.
  2. L'applicazione deve essere in grado di liberare la memoria immediatamente , senza ulteriori allocazioni accidentali nel frattempo.

    È estremamente raro che le applicazioni hanno il controllo sopra il punto 1 - Avvocenamento degli utenti mai , è un'impostazione a livello di sistema che richiede le autorizzazioni di root per modificare. 1 1 1 Igienico

    OK, quindi supponiamo che tu abbia fissato il punto 1. Quello che puoi ora fare è ad esempio utilizzare un Cache LRU per alcuni dei tuoi dati (probabilmente alcuni oggetti aziendali particolarmente grandi che possono essere rigenerati o ricaricati su richiesta). Successivamente, è necessario inserire la logica effettiva che potrebbe fallire in una funzione che supporta riprova - in altre parole, se viene abortito, puoi semplicemente rilanciarlo:

    lru_cache<widget> widget_cache;
    
    double perform_operation(int widget_id) {
        std::optional<widget> maybe_widget = widget_cache.find_by_id(widget_id);
        if (not maybe_widget) {
            maybe_widget = widget_cache.store(widget_id, load_widget_from_disk(widget_id));
        }
        return maybe_widget->frobnicate();
    }
    
    …
    
    for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) {
        try {
            return perform_operation(widget_id);
        } catch (std::bad_alloc const&) {
            if (widget_cache.empty()) throw; // memory error elsewhere.
            widget_cache.remove_oldest();
        }
    }
    
    // Handle too many failed attempts here.
    
    .

    Ma anche qui, usando std::set_new_handler invece di gestire std::bad_alloc fornisce lo stesso vantaggio e sarebbe molto più semplice.


    .

    1 se stai creando un'applicazione che fa punto di controllo 1 e stai leggendo questa risposta, ti preghiamo di spararmi un'e-mail, sono sinceramente curioso sulle tue circostanze.

Qual è il comportamento specificato standard C ++ di new in C ++?

La solita nozione è che se l'operatore new non può allocare la memoria dinamica della dimensione richiesta, quindi dovrebbe generare un'eccezione di tipo std::bad_alloc.

Tuttavia, qualcosa di più accade anche prima che venga lanciata un'eccezione bad_alloc:

C ++ 03 Sezione 3.7.4.1.3: afferma

.

Una funzione di allocazione che non riesce a allocare lo storage può richiamare l'attualmente installato new_handler (18.4.2.2), se presente. [Nota: una funzione di allocazione fornita dal programma può ottenere l'indirizzo del New_Handler attualmente installato utilizzando la funzione Set_new_Handler (18.4.2.3).] Se una funzione di allocazione dichiarata con una specifica di eccezione vuota (15.4), lancia (), non riesce Assegna lo stoccaggio, restituisce un puntatore nullo. Qualsiasi altra funzione di allocazione che non riesce a destinare lo stoccaggio deve indicare solo il fallimento del lancio di un'eccezione di classe STD :: Bad_alloc (18.4.2.1) o una classe derivata da STD :: Bad_alloc.

Considera il seguente campione di codice:

#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}
.

Nell'esempio sopra, operator new (molto probabile) non sarà in grado di allocare spazio per 100.000.000 interi interi e verrà chiamato la funzione outOfMemHandler() e il programma interromperà dopo Emissione di un messaggio di errore.

Come visto qui il comportamento predefinito dell'operatore new quando non è possibile soddisfare una richiesta di memoria, è chiamare la funzione new-handler ripetutamente finché non può trovare abbastanza memoria o non ci sono altri nuovi gestori. Nell'esempio sopra, a meno che non chiamiamo std::abort(), outOfMemHandler() sarebbe chiamato ripetutamente . Pertanto, il gestore dovrebbe assicurarsi che la prossima allocazione abbia successo o registrare un altro gestore o registrare nessun gestore o non restituire (I.e. Termina il programma). Se non c'è un nuovo gestore e l'allocazione fallisce, l'operatore lancerà un'eccezione.

Qual è il new_handler e set_new_handler?

new_handler è un typedef per un puntatore a una funzione che prende e non riporta nulla e set_new_handler è una funzione che assume e restituisce un new_handler.

Qualcosa come:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
.

Set_new_Handler's Parameter è un puntatore sull'operatore della funzione new dovrebbe chiamare se non può allocare la memoria richiesta. Il valore di ritorno è un puntatore alla funzione del gestore registrato precedentemente registrato, o NULL se non ci fosse un gestore precedente.

Come gestire le condizioni di memoria in C ++?

Dato il comportamento del programma di utenti ben progettato newa dovrebbe gestire le condizioni di memoria fornendo un corretto new_handlerwhich esegue una delle seguenti operazioni:

Maggiore più memoria disponibile: Potrebbe consentire il successivo tentativo di allocazione della memoria all'interno del loop dell'operatore Nuovo anello di successo. Un modo per implementare questo è quello di allocare un grande blocco di memoria presso l'avvio del programma, quindi rilasciarlo per l'uso nel programma la prima volta che il nuovo gestore viene invocato.

Installare un nuovo nuovo gestore: Se il nuovo gestore attuale non può rendere più disponibile la memoria, e di esserci un altro nuovo gestore che può, quindi l'attuale nuovo gestore Installa l'altro nuovo gestore al suo posto (chiamando set_new_handler). Il prossimo operatore è nuova chiamare la funzione New-Handler, otterrà l'uno più recente installato.

(una variazione su questo tema è per un nuovo gestore per modificare il proprio comportamento, quindi la prossima volta è richiamata, fa qualcosa di diverso. Un modo per ottenere questo è quello di avere il nuovo gestore di modificare statico, namespace dati specifici o globali che colpiscono il nuovo gestore

havior.)

Disinstallare il nuovo gestore: viene eseguito passando un puntatore NULL su set_new_handler.Senza un nuovo gestore installato, operator new lancerà un'eccezione ((convertibile in) std::bad_alloc) quando l'allocazione della memoria non è riuscita.

Getta un'eccezione convertibile in std::bad_alloc.Tali eccezioni non sono catturate da operator new, ma propaga al sito originando la richiesta di memoria.

Non restituzione: chiamando abort o exit.

Non lo suggerisce, poiché bad_alloc significa che sei fuori memoria .Sarebbe meglio rinunciare al fine di tentare di riprendersi.Tuttavia ecco la soluzione che stai chiedendo:

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

Posso suggerire una soluzione più semplice (e ancora più veloce) per questo.L'operatore new restituirebbe NULL se la memoria non può essere assegnata.

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

Spero che questo possa aiutare!

Lascia il tuo programma Foo Esci in un controlleratoModo:

#include <stdlib.h>     /* exit, EXIT_FAILURE */

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

Quindi scrivi un programma shell che chiama il programma effettivo.Poiché gli spazi di indirizzo sono separati, lo stato del tuo programma Shell è sempre ben definito.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top