假设我正在做一些需要在抛出异常时进行清理的事情。
例如,假设我正在创建一个动态数组,并且我需要构造对象,但它们的构造函数可能会抛出异常:
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
我应该在什么时候使用哪种技术,为什么?
(注意:这个例子只是为了展示两种技术之间的区别。我知道它不是完美的代码,所以请不要专注于这个特定的例子。这只是为了说明。)