C++ Boost ScopeExit超详细讲解

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

C++ Boost ScopeExit超详细讲解

无水先生   2022-11-19 我要评论

一、提要

资源有很多种,每种都封装一套,还是挺繁琐的!对于比较少使用或者一个程序很可能只会用一次的资源,我们不想封装,在这种情况下用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, 和20in 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(&parameter1,&parameter2,...)

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们