例外がスローされたときにクリーンアップが必要なことをしているとしましょう。
たとえば、動的配列を作成していて、オブジェクトを構築する必要があるとしますが、それらのコンストラクターが例外をスローする可能性があります。
size_t const n = 100;
T *const p = static_cast<T *>(operator new(sizeof(T) * n));
size_t i;
for (i = 0; i < n; ++i)
new (&p[i]) T(1, 2, 3); // Not exception-safe if T::T(T const &) throws!
次のいずれかで修正できますcatch (...) { ...; throw; }
:
size_t const n = 100;
T *const p = static_cast<T *>(operator new(sizeof(T) * n));
size_t i;
try
{
for (i = 0; i < n; ++i)
new (&p[i]) T(1, 2, 3);
}
catch (...)
{
while (i > 0)
p[--i].~T();
operator delete(p);
throw;
}
またはスコープ付きデストラクタを介して:
size_t n = 100;
struct Guard
{
T *p;
size_t i;
Guard(size_t n) : i(), p(static_cast<T *>(operator new(sizeof(T) * n))) { }
~Guard()
{
while (i > 0)
p[--i].~T();
operator delete(p);
}
} guard(n);
for (guard.i = 0; guard.i < n; ++guard.i)
new (&guard.p[guard.i]) T(1, 2, 3);
guard.i = 0; // Successful... "commit" the changes
guard.p = NULL; // or whatever is necessary to commit the changes
いつ、どの手法を使用するのがよいでしょうか? またその理由は?
(注:この例は、2 つの手法の違いを示すことのみを目的としています。完全なコードではないことはわかっているため、この特定の例に注目しないでください。説明のためだけです。)