C++ Boost ScopeExit超详细讲解
无水先生 人气:0一、提要
资源有很多种,每种都封装一套,还是挺繁琐的!对于比较少使用或者一个程序很可能只会用一次的资源,我们不想封装,在这种情况下用Boost.ScopeExit。
二、退出作用域(Boost.ScopeExit)
库 Boost.ScopeExit 使得在没有资源特定类的情况下使用 RAII 成为可能。
2.1 范例1.UsingBOOST_SCOPE_EXIT
#include <boost/scope_exit.hpp> #include <iostream> int *foo() { int *i = new int{10}; BOOST_SCOPE_EXIT(&i) { delete i; i = 0; } BOOST_SCOPE_EXIT_END std::cout << *i << '\n'; return i; } int main() { int *j = foo(); std::cout << j << '\n'; }
计算结果:
Boost.ScopeExit 提供了宏 BOOST_SCOPE_EXIT,它可以用来定义一些看起来像本地函数但没有名字的东西。但是,它确实有一个括号中的参数列表和大括号中的块。 必须包含头文件 boost/scoped_exit.hpp 才能使用 BOOST_SCOPE_EXIT。
宏的参数列表包含来自外部范围的变量,这些变量应该可以在块中访问。变量通过副本传递。要通过引用传递变量,它必须以 & 符号作为前缀,如示例2.1 范例1. 中所示。
如果变量在参数列表中,则块中的代码只能从外部范围访问变量。
BOOST_SCOPE_EXIT 用于定义一个块,该块将在定义块的范围结束时执行。在示例 3.1 中,使用 BOOST_SCOPE_EXIT 定义的块在 foo() 返回之前执行。
BOOST_SCOPE_EXIT 可用于从 RAII 中受益,而无需使用特定于资源的类。 foo() 使用 new 创建一个 int 变量。为了释放变量,使用 BOOST_SCOPE_EXIT 定义了一个调用 delete 的块。即使函数由于异常而提前返回,也保证执行此块。在示例 1 中,BOOST_SCOPE_EXIT 与智能指针一样好。
请注意,变量 i 在 BOOST_SCOPE_EXIT 定义的块末尾设置为 0。然后 i 由 foo() 返回并写入 main() 中的标准输出流。但是,该示例不显示 0。j 设置为随机值 - 即 int 变量在内存被释放之前所在的地址。 BOOST_SCOPE_EXIT 后面的块获得了对 i 的引用并释放了内存。但由于该块是在 foo() 的末尾执行的,因此将 0 分配给 i 为时已晚。 foo() 的返回值是在 i 设置为 0 之前创建的 i 的副本。
如果您使用 C++11 开发环境,则可以忽略 Boost.ScopeExit。在这种情况下,您可以在 lambda 函数的帮助下使用没有资源特定类的 RAII。
2.2 示例2.Boost.ScopeExit和C++11的lambda函数
#include <iostream> #include <utility> template <typename T> struct scope_exit { scope_exit(T &&t) : t_{std::move(t)} {} ~scope_exit() { t_(); } T t_; }; template <typename T> scope_exit<T> make_scope_exit(T &&t) { return scope_exit<T>{ std::move(t)}; } int *foo() { int *i = new int{10}; auto cleanup = make_scope_exit([&i]() mutable { delete i; i = 0; }); std::cout << *i << '\n'; return i; } int main() { int *j = foo(); std::cout << j << '\n'; }
运算结果:
示例2 定义了类 scope_exit,其构造函数接受一个函数。该函数由析构函数调用。此外,还定义了一个辅助函数 make_scope_exit(),它可以在无需指定模板参数的情况下实例化 scope_exit。
在 foo() 中,一个 lambda 函数被传递给 make_scope_exit()。 lambda 函数看起来像示例 3.1 中 BOOST_SCOPE_EXIT 之后的块:地址存储在 i 中的动态分配的 int 变量通过删除被释放。然后将 0 分配给 i。
该示例与前一个示例执行相同的操作。不仅 int 变量被删除,而且 j 在写入标准输出流时也没有设置为 0。
2.3 示例3.特点BOOST_SCOPE_EXIT
#include <boost/scope_exit.hpp> #include <iostream> struct x { int i; void foo() { i = 10; BOOST_SCOPE_EXIT(void) { std::cout << "last\n"; } BOOST_SCOPE_EXIT_END BOOST_SCOPE_EXIT(this_) { this_->i = 20; std::cout << "first\n"; } BOOST_SCOPE_EXIT_END } }; int main() { x obj; obj.foo(); std::cout << obj.i << '\n'; }
示例介绍了 BOOST_SCOPE_EXIT 的一些特性:
- 当 BOOST_SCOPE_EXIT 用于在一个范围内定义多个块时,这些块以相反的顺序执行。示例 3 先显示后显示。
- 如果没有变量将传递给 BOOST_SCOPE_EXIT,则需要指定 void。括号不能为空。
- 如果您在成员函数中使用 BOOST_SCOPE_EXIT 并且需要传递指向当前对象的指针,则必须使用 this_ 而不是 this。
示例3:显示first,last
, 和20
in that order.
三、练习
BOOST_SCOPE_EXIT(&flag) { } BOOST_SCOPE_EXIT_END
BOOST_SCOPE_EXIT和 BOOST_SCOPE_EXIT_END都是配合着使用的, BOOST_SCOPE_EXIT()里面可以传入多个参数。其作用相当于回调函数,
在作用域结束之后程序会自动调用 BOOST_SCOPE_EXIT到 BOOST_SCOPE_EXIT_END之间的代码。
包含的头文件是 #include <boost/scope_exit.hpp>
下面是一个简单的例子:
#include <boost/scope_exit.hpp> void PrintCout(const std::string& str) { bool submitted = false; BOOST_SCOPE_EXIT(&submitted ) { if (!submitted) std::cout<<"false"<<std::endl; else std::cout<<"true"<<std::endl; } BOOST_SCOPE_EXIT_END submitted = ((str == "Y")? true:false); } int main(int argc, char* argv[]) { std::string str; std::cin>>str; while(str != "q"){ PrintCout(str); std::cin>>str; } }
这里的 BOOST_SCOPE_EXIT()可以传入多个参数,格式:
BOOST_SCOPE_EXIT(¶meter1,¶meter2,...)
加载全部内容