web-dev-qa-db-ja.com

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

関係のない情報はほとんど省略しましたが、ここではわかりやすいと思います。

すべてのレイヤー作成を個別に試行/キャッチして、bad_allocsを再スローする前にログを記録する代わりに、手動でstd :: bad_allocをスローしても大丈夫ですか?

24
Jookia

あなたはそれをする必要はありません。 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;
}
19

質問に答えるためだけに(他の誰も答えていないように見えるため)、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)。

44
Daryl

STLコンテナでカスタムアロケータを使用する場合は、個人的にスローします。アイデアは、デフォルトのstd :: allocatorと同じインターフェース(動作の観点も含む)をSTLライブラリーに提示することです。

したがって、カスタムアロケータ(たとえば、メモリプールから割り当てるもの)があり、基になるアロケータが失敗した場合は、「throwstd :: bad_alloc」を呼び出します。これにより、99.9999%の確率でSTLコンテナーである呼び出し元が、適切にフィールド化することが保証されます。アロケーターが大きなファット0を返した場合に、これらのSTL実装が何をするかを制御することはできません。

5
Zack Yezek

もう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をスローした場合はどうなりますか?

1
MSalters