26

我有这个代码..

 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_alloc 之前记录?

4

4 回答 4

47

只是为了回答这个问题(因为似乎没有其他人回答过),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)。

于 2011-05-16T01:52:10.890 回答
20

你不需要这样做。您可以使用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;
}
于 2010-12-05T06:53:36.363 回答
5

如果我在 STL 容器中使用一些自定义分配器,我个人会抛出它。这个想法是向 STL 库提供与默认 std::allocator 相同的接口(包括行为方面)。

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

于 2013-10-18T15:42:19.057 回答
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?

于 2011-01-31T14:39:01.467 回答