首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用析构函数处理失败与catch (.){ fix();抛出;}

用析构函数处理失败与catch (.){ fix();抛出;}
EN

Stack Overflow用户
提问于 2014-03-12 09:29:20
回答 2查看 122关注 0票数 2

假设我在做一些在抛出异常时需要清理的事情。

例如,假设我正在创建一个动态数组,并且需要构造对象,但是它们的构造函数可能会抛出一个异常:

代码语言:javascript
复制
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; }修复它

代码语言:javascript
复制
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;
}

或者通过作用域析构函数。

代码语言:javascript
复制
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

我应该在什么时候使用哪种技术,为什么?

(注:这个例子只是为了说明两种技术之间的区别。我知道这不是完美的代码,所以请在这个特定的例子中做而不是。(这只是为了说明。)

EN

回答 2

Stack Overflow用户

发布于 2014-03-12 09:57:51

使用析构函数的解决方案优于显式try/catch

  • 它是可重用的,如果您需要在另一个函数中执行类似的初始化,则只需重用同一个保护类即可。
  • 维护起来更容易--假设在某些情况下,您的函数需要在失败的情况下返回,但不会抛出任何异常。对于卫士类,它或多或少是自动处理的。
  • 它更干净,因为代码更模块化。
票数 2
EN

Stack Overflow用户

发布于 2014-03-12 10:02:22

总的来说,我认为这是一个规模和安全的问题。

try/catch的问题有两个方面:

  • 安全问题:任何忽略catch (不知何故)的早期返回都无法清除
  • 缩放问题:嵌套的try/catch块使代码混乱
  • 作用域问题:要在catch中访问变量,必须在try之前定义变量,从而支持默认构造/空;这可能是痛苦的。

相反,延迟语句和卫队不会创建不必要的块/作用域,因此不会产生缩进,而是线性读取。

示例:

代码语言:javascript
复制
char buffer1[sizeof(T)];
try {
    new (buffer1) T(original);

    char buffer2[sizeof(T)];
    try {
        new (buffer2) T(original);

        // stuff here

    } catch(...) {
        reinterpret_cast<T*>(buffer2)->~T();
        throw;
    }

} catch(...) {
    reinterpret_cast<T*>(buffer1)->~T();
    throw;
}

与之相比:

代码语言:javascript
复制
char buffer1[sizeof(T)];
new (buffer1) T(original);
Defer const defer1{[&buffer1]() { reinterpret_cast<T*>(buffer1)->~T(); } };

char buffer2[sizeof(T)];
new (buffer2) T(original);
Defer const defer1{[&buffer2]() { reinterpret_cast<T*>(buffer2)->~T(); } };

// stuff here

我要指出的是,把这些概括起来似乎是个好主意:

代码语言:javascript
复制
class Guard {
public:
    explicit Guard(std::function<void()> f): _function(std::move(f)) {}

    Guard(Guard&&) = delete;
    Guard& operator=(Guard&&) = delete;

    Guard(Guard const&) = delete;
    Guard& operator=(Guard const&) = delete;

    ~Guard() {
        if (not _function) { return; }
        try { _function(); } catch(...) {}
    }

    void cancel() { _function = std::function<void()>{}; }

private:
    std::function<void()> _function;
}; // class Guard

class Defer {
public:
    explicit Defer(std::function<void()> f): _guard(std::move(f)) {}
private:
    Guard _guard;
}; // class Defer
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22347089

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档