可以手动抛出std :: bad_alloc吗?

时间:2010-12-05 06:45:31

标签: c++ new-operator throw 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;
    }
 }

我省略了大部分不相关的信息,但我认为这里的图片很清楚。

是否可以手动抛出std :: bad_alloc而不是单独尝试/捕获所有图层创建并在重新抛出bad_allocs之前进行记录?

4 个答案:

答案 0 :(得分:35)

只是回答这个问题(因为似乎没有人回答过它),C ++ 03标准定义std::bad_alloc如下:

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

由于标准定义了一个公共构造函数,因此构造并从代码中抛出一个是非常安全的。 (可以抛出具有公共拷贝构造函数的任何对象,IIRC)。

答案 1 :(得分:19)

你不需要这样做。您可以使用throw语句的无参数形式来捕获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;
}

或者,如果logger不是智能指针(它应该是):

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

答案 2 :(得分:5)

如果我在STL容器中使用一些自定义分配器,我个人会抛弃它。我们的想法是将相同的接口(包括行为方面)作为默认的std :: allocator呈现给STL库。

因此,如果您有自定义分配器(例如,从内存池分配一个)并且底层分配失败,请调用“throw std :: bad_alloc”。这保证了调用者,其中99.9999%的时间是某个STL容器,将正确地对其进行调整。如果分配器返回一个大的胖0,你无法控制那些STL实现会做什么 - 它不太可能是你想要的任何东西。

答案 3 :(得分:1)

另一种模式是使用记录器也受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.
 }

如果有多个步骤,则可以干净地扩展。您只需反复拨打.SetIntent即可。通常,您只会在CLogger::~CLogger()中写出最后一个意图字符串,但是对于额外的详细日志记录,您可以写出所有意图。

顺便说一句,在您的createEngineLayer中,您可能需要catch(...)。如果记录器抛出DiskFullException怎么办?