Va bene per lanciare manualmente uno std :: bad_alloc?
-
08-10-2019 - |
Domanda
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 credo che il quadro è chiaro qui.
Va bene per lanciare manualmente uno std :: bad_alloc invece di try / cattura tutte le creazioni di livello individuale e di registrazione prima di rethrowing bad_allocs?
Soluzione
Non c'è bisogno di fare questo. È possibile utilizzare il modulo senza parametri della dichiarazione throw
per intercettare l'eccezione std::bad_alloc
, log, quindi rigenerare esso:
logger = new CLogger(this);
try {
videoLayer = new CVideoLayer(this);
} catch (std::bad_alloc&) {
logger->log("Not enough memory to create the video layer.");
throw;
}
O, se 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;
}
Altri suggerimenti
Proprio per rispondere alla domanda (dal momento che nessun altro sembra aver risposto), il C ++ 03 definisce standard di std::bad_alloc
come segue:
namespace std {
class bad_alloc : public exception {
public:
bad_alloc() throw();
bad_alloc(const bad_alloc&) throw();
bad_alloc& operator=(const bad_alloc&) throw();
virtual ˜bad_alloc() throw();
virtual const char* what() const throw();
};
}
Dal momento che le definisce standard di un costruttore pubblico, si sarebbe perfettamente sicuro per costruire e lanciare uno dal codice. (Qualsiasi oggetto con un costruttore di copia pubblica può essere gettato, IIRC).
Io personalmente non gettarlo se uso un po 'allocatore personalizzato in contenitori STL. L'idea è di presentare lo stesso interface- anche in termini di comportamento-alle librerie STL come il default std :: allocator.
Quindi, se si dispone di un allocatore personalizzato (per esempio, una allocazione da un pool di memoria) e il sottostante allocare non riesce, chiamata "tiro std :: bad_alloc". Questo garantisce il chiamante, che 99,9999% del tempo è un po 'contenitore STL, metterà in campo in modo corretto. Lei ha alcun controllo su quali siano tali implementazioni STL faranno se l'allocatore restituisce un grosso grasso 0- è improbabile che possa essere qualcosa che ti piace.
Un altro modello è quello di utilizzare il fatto che lo strumento è soggetto a Raii anche:
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 scala in modo pulito se ci sono più passaggi. Basta chiamare .SetIntent
ripetutamente. Normalmente, si scrive solo l'ultima stringa intenti CLogger::~CLogger()
ma per extra verbose la registrazione è possibile scrivere tutti gli effetti.
A proposito, nel vostro createEngineLayer
si potrebbe desiderare un catch(...)
. Che cosa succede se il logger lancia un DiskFullException
?