2010-12-05 13 views
20

ich diesen Code haben ..Ist es in Ordnung, einen Std :: Bad_alloc manuell zu werfen?

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

Ich habe die meisten der nicht-verwandten Informationen weggelassen, aber ich denke, das Bild klar ist hier.

Ist es in Ordnung, manuell ein Std :: bad_alloc zu werfen, anstatt alle Layer-Kreationen einzeln zu erfassen und abzufragen, bevor bad_allocs erneut gestartet wird?

+0

Eine kleine Anmerkung, wenn Sie nicht einen intelligenten Zeiger für Logger verwenden, dann wird dies leckt, wenn CVideoLayer Konstruktor wirft. –

+0

Ich habe den Video Layer Teil bearbeitet, da ich eigentlich noch keinen Video Layer habe und mein Problem zeigen wollte. Ich entschied mich dafür, es einfacher zu machen, anstatt genau zu sein. – Jookia

Antwort

18

Sie müssen das nicht tun. Sie können die parameterlos Form der throw Anweisung verwenden, um die std::bad_alloc Ausnahme abzufangen, melden Sie es, dann erneut auslösen es:

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

Oder, wenn logger kein Smart-Pointer ist (was es sein sollte):

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; mach es so - mach Ausnahmen für dich funktionieren nicht für Ausnahmen. –

+1

Wenn Logger kein intelligenter Zeiger ist (was es sein sollte), würde ich auch 'catch (...) {delete logger; throw;}' hinzufügen. Nur für den Fall, dass der CVideoLayer-Konstruktor eine weitere Ausnahme auslöst. Wenn Ihr Objekt mehr als eine Ressource (Zeiger) verwalten soll, dann muss es intelligente Zeiger verwenden, ansonsten wird die Implementierung des Konstruktors sehr kompliziert. –

+0

@Martin, du hast absolut recht. Antwort aktualisiert, danke :) –

1

Ein weiteres Muster ist die Tatsache zu nutzen, dass der Logger RAII unterliegt auch:

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

Diese Waage sauber, wenn es multip sind le Schritte. Sie rufen einfach .SetIntent wiederholt an. Normalerweise schreiben Sie nur die letzte Intent-Zeichenkette in CLogger::~CLogger() aus, aber für eine zusätzliche ausführliche Protokollierung können Sie alle Intents schreiben.

BTW, in Ihrem createEngineLayer möchten Sie vielleicht eine catch(...). Was passiert, wenn der Logger ein DiskFullException wirft?

30

einfach die Frage zu beantworten (da niemand sonst scheint es beantwortet zu haben), die C++ 03-Standard definiert std::bad_alloc wie folgt:

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

Da der Standard einen öffentlichen Konstruktor definiert, dann würden Sie sein absolut sicher zu konstruieren und einen aus Ihrem Code zu werfen. (Jedes Objekt mit einem öffentlichen Kopierkonstruktor kann geworfen werden, IIRC).

+7

Dies ist die Antwort, die ich suchte, als ich den Titel der Frage in meiner Suche sah Motor – d11

5

Ich persönlich werfen es, wenn ich einige benutzerdefinierte Zuordner in STL-Containern verwenden. Die Idee ist, den STL-Bibliotheken dieselbe Schnittstelle - einschließlich des Verhaltens - als Standard-std :: allocator zur Verfügung zu stellen.

Wenn Sie also einen benutzerdefinierten Zuordner haben (z. B. einen, der aus einem Speicherpool reserviert) und die zugrunde liegende Zuweisung fehlschlägt, rufen Sie "throw std :: bad_alloc" auf. Das garantiert dem Anrufer, der zu 99.9999% ein STL-Container ist, dass er es richtig auffängt. Sie haben keine Kontrolle darüber, was diese STL-Implementierungen tun werden, wenn der Allokator ein großes Fett 0 zurückgibt - es ist unwahrscheinlich, dass es etwas ist, das Sie mögen.

Verwandte Themen