c++ - with - terminate called after throwing an instance of 'std::bad_alloc'




Va bene buttare manualmente uno std:: bad_alloc? (3)

Io personalmente lo tiro se uso qualche allocatore personalizzato nei contenitori STL. L'idea è di presentare la stessa interfaccia, inclusa in termini di comportamento, alle librerie STL come predefinito std :: allocator.

Pertanto, se si dispone di un allocatore personalizzato (ad esempio, un'allocazione da un pool di memoria) e l'allocazione sottostante non riesce, chiamare "throw std :: bad_alloc". Ciò garantisce al chiamante, che il 99,9999% delle volte è un contenitore STL, lo inserirà correttamente. Non hai alcun controllo su ciò che queste implementazioni STL faranno se l'allocatore restituisce un grosso grasso 0 - è improbabile che sia qualcosa che ti piacerà.

Ho questo codice ..

 CEngineLayer::CEngineLayer(void)
 {
    // Incoming creation of layers. Wrapping all of this in a try/catch block is
    // not helpful if logging of errors will happen.

    logger = new (std::nothrow) CLogger(this);

    if(logger == 0)
    {
     std::bad_alloc exception;
     throw exception;
    }

    videoLayer = new (std::nothrow) CVideoLayer(this);

    if(videoLayer == 0)
    {
     logger->log("Unable to create the video layer!");

     std::bad_alloc exception;
     throw exception;
    }
 }

 IEngineLayer* createEngineLayer(void)
 {
    // Using std::nothrow would be a bad idea here as catching things thrown
    // from the constructor is needed.

    try
    {
     CEngineLayer* newLayer = new CEngineLayer;

     return (IEngineLayer*)newLayer;
    }
    catch(std::bad_alloc& exception)
    {
     // Couldn't allocate enough memory for the engine layer.
     return 0;
    }
 }

Ho omesso la maggior parte delle informazioni non correlate, ma penso che l'immagine sia chiara qui.

Va bene buttare manualmente uno std :: bad_alloc invece di provare / catturare tutte le creazioni del livello individualmente e loggare prima di rilanciare bad_allocs?


Non hai bisogno di farlo. È possibile utilizzare il modulo senza parametri std::bad_alloc throw per std::bad_alloc eccezione std::bad_alloc , registrarla e quindi ricrearla:

logger = new CLogger(this);
try {
    videoLayer = new CVideoLayer(this);
} catch (std::bad_alloc&) {
    logger->log("Not enough memory to create the video layer.");
    throw;
}

Oppure, se il logger non è un puntatore intelligente (che dovrebbe essere):

logger = new CLogger(this);
try {
    videoLayer = new CVideoLayer(this);
} catch (std::bad_alloc&) {
    logger->log("Not enough memory to create the video layer.");
    delete logger;
    throw;
} catch (...) {
    delete logger;
    throw;
}

Un altro esempio è usare il fatto che anche il logger è soggetto a RAII:

CEngineLayer::CEngineLayer( )
 {
   CLogger logger(this); // Could throw, but no harm if it does.
   logger.SetIntent("Creating the video layer!");
   videoLayer = new CVideoLayer(this);
   logger.SetSucceeded(); // resets intent, so CLogger::~CLogger() is silent.
 }

Questo ridimensiona in modo pulito se ci sono più passaggi. Basta chiamare .SetIntent ripetutamente. Normalmente, si scrive solo l'ultima stringa di intento in CLogger::~CLogger() ma per la registrazione extra dettagliata è possibile scrivere tutti gli intenti.

A proposito, nel tuo createEngineLayer potresti volere una catch(...) . Cosa succede se il logger lancia un DiskFullException ?





bad-alloc