2010-12-05 21 views
20

tengo este código ..¿Está bien lanzar manualmente un std :: bad_alloc?

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 es clara aquí.

¿Está bien lanzar manualmente un std :: bad_alloc en lugar de intentar/capturar todas las creaciones de capa individualmente y el inicio de sesión antes de volver a lanzar bad_allocs?

+0

Una pequeña nota, si no está utilizando un puntero inteligente para el registrador, entonces se perderá si se lanza el constructor de CVideoLayer. –

+0

Edité la parte de la capa de video porque aún no tengo una capa de video (todavía) y quería mostrar mi problema. Decidí hacerlo simple en lugar de preciso. – Jookia

Respuesta

18

No necesita hacer eso. Usted puede utilizar el formulario sin parámetros de la declaración throw para detectar la excepción std::bad_alloc, ingrese, luego volver a lanzar que:

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 logger no es un puntero inteligente (que debe 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; 
} 
+0

+1; hazlo de esta manera: haz que las excepciones funcionen para ti; no trabaje por excepciones –

+1

Si el registrador no es un puntero inteligente (que debería ser), también agregaría 'catch (...) {delete logger; throw;}' En caso de que el constructor CVideoLayer lanzara otra excepción. Si su objeto va a administrar más de un recurso (puntero), entonces necesita utilizar punteros inteligentes, de lo contrario, el constructor se vuelve muy complicado de implementar correctamente. –

+0

@Martin, tienes toda la razón. Respuesta actualizada, gracias :) –

1

Otro patrón es usar el hecho de que el registrador está sujeta a RAII, también:

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

Esta escalas limpiamente si hay multípara le pasos. Simplemente llama al .SetIntent repetidamente. Normalmente, solo escribe la última serie de intentos en CLogger::~CLogger(), pero para el registro extra detallado puede escribir todos los intentos.

BTW, en su createEngineLayer es posible que desee un catch(...). ¿Qué pasa si el registrador arroja un DiskFullException?

30

Sólo para responder a la pregunta (ya que nadie más parece haber contestado), los 03 C++ estándar define std::bad_alloc de la 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 la norma define un constructor público, serías perfectamente seguro para construir y lanzar uno desde tu código. (Cualquier objeto con un constructor de copia pública puede ser lanzado, IIRC).

+7

Esta es la respuesta que vine buscando cuando vi el título de la pregunta en mi búsqueda motor – d11

5

Yo personalmente lo tiro 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 estándar std :: allocator.

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

Cuestiones relacionadas