operator new example delete bad_alloc c++ new-operator throw bad-alloc

c++ - new - ¿Está bien lanzar manualmente un std:: bad_alloc?



new keyword c++ (4)

Tengo este codigo ..

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; } }

He omitido la mayor parte de la información no relacionada, pero creo que la imagen está clara aquí.

¿Está bien lanzar manualmente un std :: bad_alloc en lugar de intentar / capturar todas las creaciones de capas individualmente y registrarlas antes de volver a generar bad_allocs?


No necesitas hacer eso. Puede usar la forma sin parámetros de la instrucción throw para capturar la excepción std::bad_alloc , std::bad_alloc y luego std::bad_alloc :

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, si el logger no es un puntero inteligente (que debería ser):

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; }


Otro patrón es usar el hecho de que el registrador también está sujeto 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. }

Esto se limpia limpiamente si hay varios pasos. Solo tienes que llamar a .SetIntent repetidamente. Normalmente, solo escribe la cadena del último intento en CLogger::~CLogger() pero para un registro detallado adicional puede escribir todos los intentos.

Por cierto, en su createEngineLayer es posible que desee un catch(...) . ¿Qué pasa si el registrador lanza una DiskFullException ?


Solo para responder la pregunta (ya que nadie más parece haberla respondido), el estándar C ++ 03 define std::bad_alloc siguiente manera:

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(); }; }

Dado que el estándar define un constructor público, sería perfectamente seguro construir y lanzar uno desde su código. (Cualquier objeto con un constructor de copia pública puede ser lanzado, IIRC).


Yo personalmente LO lanzo si uso algún asignador personalizado en contenedores STL. La idea es presentar la misma interfaz, incluso en términos de comportamiento, a las bibliotecas STL como el std :: allocator predeterminado.

Por lo tanto, si tiene un asignador personalizado (por ejemplo, una asignación de un grupo de memoria) y la asignación subyacente falla, llame "throw std :: bad_alloc". Eso garantiza que la persona que llama, que el 99.9999% del tiempo es algún contenedor STL, lo enviará correctamente. No tiene control sobre lo que harán esas implementaciones de STL si el asignador devuelve un gran 0, es poco probable que sea algo que le guste.