【问题标题】:Is it okay to manually throw an std::bad_alloc?手动抛出 std::bad_alloc 可以吗?
【发布时间】:2011-05-20 10:52:55
【问题描述】:

我有这个代码..

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

【问题讨论】:

  • 一个小提示,如果你没有为记录器使用智能指针,那么如果 CVideoLayer 的构造函数抛出,这将泄漏。
  • 我编辑了视频层部分,因为我实际上还没有视频层,并且想展示我的问题。我决定让它简单而不是准确。

标签: c++ new-operator throw bad-alloc


【解决方案1】:

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

【讨论】:

  • 这是我在搜索引擎中看到问题标题时寻找的答案
【解决方案2】:

您不需要这样做。您可以使用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;
}

【讨论】:

  • 如果 logger 不是智能指针(它应该是),那么我还要添加 catch(...) { delete logger;throw;} 以防 CVideoLayer 构造函数抛出另一个异常。如果您的对象要管理多个资源(指针),那么它需要使用智能指针,否则构造函数会变得非常复杂,无法正确实现。
  • 如果分配抛出std::bad_alloc,则logger 保证为NULL?因为如果不是这样,您就有删除悬空指针的风险。
  • @Idan,很好,logger 必须显式初始化为 NULL 以确保安全。答案已相应更新,谢谢:)
  • 通常情况下,如果您遇到甚至无法记录正在发生的事情的情况,您仍想尽快调用终止
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    另一种模式是使用记录器也受 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 怎么办?

    【讨论】:

      猜你喜欢
      • 2012-08-08
      • 1970-01-01
      • 2012-08-24
      • 2016-04-04
      • 2011-12-25
      • 1970-01-01
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多