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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4357719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:13:34  来源:igfitidea点击:

Is it okay to manually throw an std::bad_alloc?

c++new-operatorthrowbad-alloc

提问by Jookia

I have this code..

我有这个代码..

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

I've omitted most of the non-related information, but I think the picture is clear here.

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

Is it okay to manually throw an std::bad_alloc instead of try/catching all of the layer creations individually and logging before rethrowing bad_allocs?

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

采纳答案by Frédéric Hamidi

You don't need to do that. You can use the parameterless form of the throwstatement to catch the std::bad_allocexception, log it, then rethrow it:

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

Or, if loggeris not a smart pointer (which it should be):

或者,如果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;
}

回答by Daryl

Just to answer the question (since nobody else seems to have answered it), the C++03 standard defines std::bad_allocas follows:

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

Since the standard defines a public constructor, you'd be perfectly safe to construct and throw one from your code. (Any object with a public copy constructor can be thrown, IIRC).

由于标准定义了一个公共构造函数,因此您可以完全安全地从代码中构造和抛出一个构造函数。(任何具有公共复制构造函数的对象都可以抛出,IIRC)。

回答by Zack Yezek

I personally DO throw it if I use some custom allocator in STL containers. The idea is to present the same interface- including in terms of behavior- to the STL libraries as the default std::allocator.

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

So, if you have a custom allocator (say, one allocating from a memory pool) and the underlying allocate fails, call "throw std::bad_alloc". That guarantees the caller, who 99.9999% of the time is some STL container, will field it properly. You have no control over what those STL implementations will do if the allocator returns a big fat 0- it is unlikely to be anything you'll like.

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

回答by MSalters

Another pattern is to use the fact that the logger is subject to RAII, too:

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

This scales cleanly if there are multiple steps. You just call .SetIntentrepeatedly. Normally, you only write out the last intent string in CLogger::~CLogger()but for extra verbose logging you can write out all intents.

如果有多个步骤,这可以干净地缩放。你只是.SetIntent反复打电话。通常,您只写出最后一个意图字符串,CLogger::~CLogger()但对于更详细的日志记录,您可以写出所有意图。

BTW, in your createEngineLayeryou might want a catch(...). What if the logger throws a DiskFullException?

顺便说一句,在createEngineLayer你可能想要一个catch(...). 如果记录器抛出DiskFullException?