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

C++ 手动抛出std::bad_alloc可以吗?,c++,new-operator,throw,bad-alloc,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)

我有这个密码

 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之前尝试/捕获所有层创建并记录,可以吗?

您不需要这样做。您可以使用
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
不是智能指针(应该是):


另一种模式是使用记录器也受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
。通常,您只能在
阻塞器::~clouger()
中写出最后一个意图字符串,但对于额外的详细日志记录,您可以写出所有意图


顺便说一句,在
createEngineLayer
中,您可能需要一个
捕获(…)
。如果记录器抛出一个
DiskFullException
,该怎么办

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

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


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

一个小提示,如果您没有为记录器使用智能指针,那么如果CVideoLayer的构造函数抛出,这将泄漏。我在中编辑了视频层部分,因为我实际上还没有视频层(尚未)想展示我的问题。我决定让它变得简单而不是准确。如果记录器不是一个智能指针(它应该是),那么我还将添加
catch(…){delete logger;throw;}
,以防CVideoLayer构造函数引发另一个异常。如果您的对象要管理多个资源(指针),则需要使用智能指针,否则构造函数将变得非常复杂,无法正确实现。如果其分配抛出
std::bad_alloc
,是否保证
logger
为空?因为如果不是这样,您就有可能删除一个悬空指针。@Idan,很好的一点,
logger
必须显式初始化为
NULL
,以确保安全。答案相应更新,谢谢:)一般情况下,如果你甚至无法记录正在发生的事情,你想尽快打电话终止。这就是我在搜索引擎中看到问题标题时寻找的答案
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();
  };
}