私はこのコードを持っています。
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をスローしても大丈夫ですか?
あなたはそれをする必要はありません。 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;
}
質問に答えるためだけに(他の誰も答えていないように見えるため)、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コンテナでカスタムアロケータを使用する場合は、個人的にスローします。アイデアは、デフォルトのstd :: allocatorと同じインターフェース(動作の観点も含む)をSTLライブラリーに提示することです。
したがって、カスタムアロケータ(たとえば、メモリプールから割り当てるもの)があり、基になるアロケータが失敗した場合は、「throwstd :: bad_alloc」を呼び出します。これにより、99.9999%の確率でSTLコンテナーである呼び出し元が、適切にフィールド化することが保証されます。アロケーターが大きなファット0を返した場合に、これらのSTL実装が何をするかを制御することはできません。
もう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
をスローした場合はどうなりますか?