类库大魔王
类库大魔王 懒惰,傲慢,以及无耐心

C++中RAII的实现手法

Go语言中有defer可以在退出当前作用域时执行一个函数调用,C++中以前常用的做法是创建一个类的对象,在该类的析构函数中写入需要执行的代码。而这个对象可以创建在栈中,也可以放在std::auto_prt<>std::unique_prt<>之类的地方,只要退出作用域就会被销毁就可以。这在网上能找到不少的讨论,比如C++之父的这段,C++ Core Guidelines中的这段,以及Cpp Reference中的这篇

Boost中也提供了BOOST_SCOPE_EXIT宏,但是用起来相当丑陋,而且像稍早版本的Qt Creator内置的简单的C++语法解析器甚至不能正常解析:

1
2
3
4
5
6
7
{ // Some local scope.
    ...
    BOOST_SCOPE_EXIT(capture_list) {
        ... // Body code.
    } BOOST_SCOPE_EXIT_END
    ...
}

到了C++11,这个宏有了改进,使用lambda的机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void world::add_person(person const& a_person) {
    persons_.push_back(a_person);

    // This block must be no-throw.
    person& p = persons_.back();
    person::evolution_t checkpoint = p.evolution;
    // Capture all by reference `&`, but `checkpoint` and `this` (C++11 only).
    BOOST_SCOPE_EXIT_ALL(&, checkpoint, this) { // Use `this` (not `this_`).
        if(checkpoint == p.evolution) this->persons_.pop_back();
    }; // Use `;` (not `SCOPE_EXIT_END`).

    // ...

    checkpoint = ++p.evolution;

    // Assign new identifier to the person.
    person::id_t const prev_id = p.id;
    p.id = next_id_++;
    // Capture all by value `=`, but `p` (C++11 only).
    BOOST_SCOPE_EXIT_ALL(=, &p) {
        if(checkpoint == p.evolution) {
            this->next_id_ = p.id;
            p.id = prev_id;
        }
    };

    // ...

    checkpoint = ++p.evolution;
}

既然这样,其实我们自己也不是一定要用Boost的实现,可以自己简单实现一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <functional>

class ScopedGuard
{
    std::function<void(void)> m_f;
public:
    ScopedGuard() = delete;
    ScopedGuard(ScopedGuard&&) =delete;
    ScopedGuard(const ScopedGuard&) =delete;
    ScopedGuard& operator=(ScopedGuard&&)=delete;
    ScopedGuard& operator=(const ScopedGuard&)=delete;
    ScopedGuard(std::function<void(void)> f) : m_f(f) {}
    ~ScopedGuard() { m_f(); }
};

使用示例:

1
2
3
ScopedGuard queryMutexUnlock([this](){m_queryMutex.unlock();});

ScopedGuard postFinishedQueryEvent([this, e](){QCoreApplication::postEvent(this, e);});

有了lambda就是方便。

感觉本文不错,不妨小额鼓励我一下!
支付宝扫一扫

支付宝扫一扫

微信扫一扫

微信扫一扫

如果你看不到评论框,说明Disqus被墙了。