要开始使用 Boost.Function, 就要包含头文件 "boost/function.hpp", 或者某个带数字的版本,从"boost/function/function0.hpp" 到 "boost/function/function10.hpp". 如果你知道你想保存在 function 中的函数的参数数量,这样做可以让编译器仅包含需要的头文件。如果包含 "boost/function.hpp", 那么就会把其它的头文件也包含进去。

理解被存函数的最佳方法是把它想象为一个普通的函数对象,该函数对象用于封装另一个函数(或函数对象)。这个被存的函数的最大用途是它可以被多次调用,而无须在创建 function 时立即使用。在声明 functions 时,声明中最重要的部分是函数的签名。这部分即是告诉 function 它将保存的函数或函数对象的签名和返回类型。我们已经看到,有两种方法来执行这个声明。这里有一个完整的程序,程序声明了一个 boost::function ,它可以保存返回bool (或某个可以隐式转换为 bool 的类型)并接受两个参数的类函数实体,第一个参数可以转换为 int, 第二个参数可以转换为 double.

[cpp] view plain copy
  1. #include <iostream>
  2. #include "boost/function.hpp"
  3. bool some_func(int i,double d) {
  4. return i>d;
  5. }
  6. int main() {
  7. boost::function<bool (int,double)> f;
  8. f=&some_func;
  9. f(10,1.1);
  10. }

当 function f 首次创建时,它不保存任何函数。它是空的,可以在一个布尔上下文中进行测试。如果你试图调用一个没有保存任何函数或函数对象的 function ,它将抛出一个类型 bad_function_call 的异常。为了避免这个问题,我们用普通的赋值语法把一个指向 some_func 的指针赋值给 f 。这导致 f 保存了到 some_func 的指针。最后,我们用参数10 (一个 int) 和 1.1 (一个 double)来调用 f (用函数调用操作符)。要调用一个 function, 你必须提供被存函数或函数对象所期望的准确数量的参数。

回调的基础

我们先来看看在没有 Boost.Function 以前我们如何实现一个简单的回调,然后再把代码改为使用 function, 并看看会带来什么优势。我们从一个支持某种简单的回调形式的类开始,它可以向任何对新值关注的对象报告值的改变。这里的回调是一种传统的C风格回调,即使用普通函数。这种回调用可用于象GUI控制这样的场合,它可以通知观察者用户改变了它的值,而不需要对监听该信息的客户有任何特殊的知识。

[cpp] view plaincopy
  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include "boost/function.hpp"
  5. void print_new_value(int i) {
  6. std::cout <<
  7. "The value has been updated and is now " << i << '/n';
  8. }
  9. void interested_in_the_change(int i) {
  10. std::cout << "Ah, the value has changed./n";
  11. }
  12. class notifier {
  13. typedef void (*function_type)(int);
  14. std::vector<function_type> vec_;
  15. int value_;
  16. public:
  17. void add_observer(function_type t) {
  18. vec_.push_back(t);
  19. }
  20. void change_value(int i) {
  21. value_=i;
  22. for (std::size_t i=0;i<vec_.size();++i) {
  23. (*vec_[i])(value_);
  24. }
  25. }
  26. };
  27. int main() {
  28. notifier n;
  29. n.add_observer(&print_new_value);
  30. n.add_observer(&interested_in_the_change);
  31. n.change_value(42);
  32. }

这里的两个函数,print_new_value 和 interested_in_the_change, 它们的函数签名都兼容于 notifier 类的要求。这些函数指针被保存在一个 vector 内,并且无论何时它的值被改变,这些函数都会在一个循环里被调用。调用这些函数的一种语法是:

(*vec_[i])(value_);

值(value_)被传递给解引用的函数指针(即 vec_[i] 所返回的)。另一种写法也是有效的,即这样:

vec_[i](value_);

这种写法看起来更好看些,但更为重要的是,它还可以允许你把函数指针更换为 Boost.Function 而没有改变调用的语法。现在,工作还是正常的,但是,唉,函数对象不能用于这个 notifier 类。事实上,除了函数指针以外,别的任何东西都不能用,这的确是一种局限。但是,如果我们使用 Boost.Function,它就可以工作。重写这个 notifier类非常容易。

[cpp] view plaincopy
  1. class notifier {
  2. typedef boost::function<void(int)> function_type;
  3. std::vector<function_type> vec_;
  4. int value_;
  5. public:
  6. template <typename T> void add_observer(T t) {
  7. vec_.push_back(function_type(t));
  8. }
  9. void change_value(int i) {
  10. value_=i;
  11. for (std::size_t i=0;i<vec_.size();++i) {
  12. vec_[i](value_);
  13. }
  14. }
  15. };

首先要做的事是,把 typedef 改为代表 boost::function 而不是函数指针。之前,我们定义的是一个函数指针;现在,我们使用泛型方法,很快就会看到它的用途。接着,我们把成员函数 add_observer 的签名改为泛化的参数类型。我们也可以把它改为接受一个 boost::function,但那样会要求该类的用户必须也知道 function 的使用方法[2],而不是仅仅知道这个观察者类型的要求就行了。应该注意到 add_observer 的这种变化并不应该是转向function 的结果;无论如何代码应该可以继续工作。我们把它改为泛型的;现在,不管是函数指针、函数对象,还是 boost::function 实例都可以被传递给 add_observer, 而无须对已有用户代码进行任何改动。把元素加入到vector 的代码有一些修改,现在需要创建一个 boost::function<void(int)> 实例。最后,我们把调用这些函数的语法改为可以使用函数、函数对象以及 boost::function 实例[3]。这种对不同类型的类似函数的"东西"的扩展支持可以立即用于带状态的函数对象,它们可以实现一些用函数很难做到的事情。

[2] 他们应该知道 Boost.Function,但如果他们不知道呢?我们添加到接口上的任何东西都必须及时向用户解释清楚。

[3] 现在我们知道,一开始我们就应该用这种语法。

[cpp] view plaincopy
  1. class knows_the_previous_value {
  2. int last_value_;
  3. public:
  4. void operator()(int i) {
  5. static bool first_time=true;
  6. if (first_time) {
  7. last_value_=i;
  8. std::cout <<
  9. "This is the first change of value, /
  10. so I don't know the previous one./n";
  11. first_time=false;
  12. return;
  13. }
  14. std::cout << "Previous value was " << last_value_ << '/n';
  15. last_value_=i;
  16. }
  17. };

这个函数对象保存以前的值,并在值被改变时把旧值输出到 std::cout 。注意,当它第一次被调用时,它并不知道旧值。这个函数对象在函数中使用一个静态 bool 变量来检查这一点,该变量被初始化为 true. 由于函数中的静态变量是在函数第一次被调用时进行初始化的,所以它仅在第一次调用时被设为 true 。虽然也可以在普通函数中使用静态变量来提供状态,但是我们必须知道那样不太好,而且很难做到多线程安全。因此,带状态的函数对象总是优于带静态变量的普通函数。notifier 类并不关心这是不是函数对象,只要符合要求就可以接受。以下更新的例子示范了它如何使用。

[cpp] view plaincopy
  1. int main() {
  2. notifier n;
  3. n.add_observer(&print_new_value);
  4. n.add_observer(&interested_in_the_change);
  5. n.add_observer(knows_the_previous_value());
  6. n.change_value(42);
  7. std::cout << '/n';
  8. n.change_value(30);
  9. }

关键一点要注意的是,我们新增的一个观察者不是函数指针,而是一个 knows_the_previous_value 函数对象的实例。运行这段程序的输出如下:

The value has been updated and is now 42
Ah, the value has changed.
This is the first change of value, so I don't know the previous one.The value has been updated and is now 30
Ah, the value has changed.
Previous value was 42

在这里最大的优点不是放宽了对函数的要求(或者说,增加了对函数对象的支持),而是我们可以使用带状态的对象,这是非常需要的。我们对 notifier 类所做的修改非常简单,而且用户代码不受影响。如上所示,把 Boost.Function 引入一个已有的设计中是非常容易的。

类成员函数

Boost.Function 不支持参数绑定,这在每次调用一个 function 就要调用同一个类实例的成员函数时是需要的。幸运的是,如果这个类实例被传递给 function 的话,我们就可以直接调用它的成员函数。这个 function 的签名必须包含类的类型以及成员函数的签名。换言之,显式传入的类实例要作为隐式的第一个参数,this。这样就得到了一个在给出的对象上调用成员函数的函数对象。看一下以下这个类:

[cpp] view plaincopy
  1. class some_class {
  2. public:
  3. void do_stuff(int i) const {
  4. std::cout << "OK. Stuff is done. " << i << '/n';
  5. }
  6. };

成员函数 do_stuff 要从一个 boost::function 实例里被调用。要做到这一点,我们需要 function 接受一个some_class 实例,签名的其它部分为一个 void 返回以及一个 int 参数。对于如何把 some_class 实例传给 function,我们有三种选择:传值,传引用,或者传址。如何要传值,代码就应该这样写[4]

[4] 很少会有理由来以传值的方式传递对象参数。

[cpp] view plaincopy
  1. boost::function<void(some_class,int)> f;

注意,返回类型仍旧在最开始,后跟成员函数所在的类,最后是成员函数的参数类型。它就象传递一个 this 给一个函数,该函数暗地里用类实例调用一个非成员函数。要把函数 f 配置为成员函数 do_stuff, 然后调用它,我们这样写:

[cpp] view plaincopy
  1. f=&some_class::do_stuff;
  2. f(some_class(),2);

如果要传引用,我们要改一下函数的签名,并传递一个 some_class 实例。

[cpp] view plaincopy
  1. boost::function<void(some_class&,int)> f;
  2. f=&some_class::do_stuff;
  3. some_class s;
  4. f(s,1);

最后,如果要传 some_class 的指针[5],我们就要这样写:

[5] 裸指针或智能指针皆可。

[cpp] view plaincopy
  1. boost::function<void(some_class*,int)> f;
  2. f=&some_class::do_stuff;
  3. some_class s;
  4. f(&s,3);

好了,所有这些传递"虚拟 this"实例的方法都已经在库中提供。当然,这种技术也是有限制的:你必须显式地传递类实例;而理想上,你更愿意这个实例被绑定在函数中。乍一看,这似乎是 Boost.Function 的缺点,但有别的库可以支持参数的绑定,如 Boost.Bind 和 Boost.Lambda. 我们将在本章稍后的地方示范这些库会给 Boost.Function 带有什么好处。

带状态的函数对象

我们已经看到,由于支持了函数对象,就可以给回调函数增加状态。考虑这样一个类,keeping_state, 它是一个带状态的函数对象。keeping_state 的实例记录一个总和,它在每次调用操作符执行时被增加。现在,将该类的一个实例用于两个 boost::function 实例,结果有些出人意外。

[cpp] view plaincopy
  1. #include <iostream>
  2. #include "boost/function.hpp"
  3. class keeping_state {
  4. int total_;
  5. public:
  6. keeping_state():total_(0) {}
  7. int operator()(int i) {
  8. total_+=i;
  9. return total_;
  10. }
  11. int total() const {
  12. return total_;
  13. }
  14. };
  15. int main() {
  16. keeping_state ks;
  17. boost::function<int(int)> f1;
  18. f1=ks;
  19. boost::function<int(int)> f2;
  20. f2=ks;
  21. std::cout << "The current total is " << f1(10) << '/n';
  22. std::cout << "The current total is " << f2(10) << '/n';
  23. std::cout << "After adding 10 two times, the total is "
  24. << ks.total() << '/n';
  25. }

写完这段代码并接着执行它,程序员可能期望保存在 ks 的总和是20,但不是;事实上,总和为0。以下是这段程序的运行结果。

The current total is 10
The current total is 10
After adding 10 two times, the total is 0

原因是每一个 function 实例(f1 和 f2)都含有一个 ks 的拷贝,这两个实例得到的总和都是10,但 ks 没有变化。这可能是也可能不是你想要的,但是记住,boost::function 的缺省行为是复制它要调用的函数对象,这一点很重要。如果这导致不正确的语义,或者如果某些函数对象的复制代价太高,你就必须把函数对象包装在boost::reference_wrapper 中,那样 boost::function 的复制就会是一个 boost::reference_wrapper 的拷贝,它恰好持有一个到原始函数对象的引用。你无须直接使用 boost::reference_wrapper ,你可以使用另两个助手函数,ref 和 cref。 这两函数返回一个持有到某特定类型的引用或 const 引用的 reference_wrapper。在前例中,要获得我们想要的语义,即使用同一个 keeping_state 实例,我们就需要把代码修改如下:

[cpp] view plaincopy
  1. int main() {
  2. keeping_state ks;
  3. boost::function<int(int)> f1;
  4. f1=boost::ref(ks);
  5. boost::function<int(int)> f2;
  6. f2=boost::ref(ks);
  7. std::cout << "The current total is " << f1(10) << '/n';
  8. std::cout << "The current total is " << f2(10) << '/n';
  9. std::cout << "After adding 10 two times, the total is "
  10. << ks.total() << '/n';
  11. }

boost::ref 的用途是通知 boost::function,我们想保存一个到函数对象的引用,而不是一个拷贝。运行这个程序有以下输出:

The current total is 10
The current total is 20
After adding 10 two times, the total is 20

这正是我们想要的结果。使用 boost::ref 和 boost::cref 的不同之处就象引用与 const 引用的差异,对于后者,你只能调用其中的常量成员函数。以下例子使用一个名为 something_else 的函数对象,它有一个 const 的调用操作符。

[cpp] view plaincopy
  1. class something_else {
  2. public:
  3. void operator()() const {
  4. std::cout << "This works with boost::cref/n";
  5. }
  6. };

对于这个函数对象,我们可以使用 boost::ref 或 boost::cref.

[cpp] view plaincopy
  1. something_else s;
  2. boost::function0<void> f1;
  3. f1=boost::ref(s);
  4. f1();
  5. boost::function0<void> f2;
  6. f2=boost::cref(s);
  7. f2();

如果我们改变了 something_else 的实现,使其函数为非const, 则只有 boost::ref 可以使用,而 boost::cref 将导致一个编译期错误。

[cpp] view plaincopy
  1. class something_else {
  2. public:
  3. void operator()() {
  4. std::cout <<
  5. "This works only with boost::ref, or copies/n";
  6. }
  7. };
  8. something_else s;
  9. boost::function0<void> f1;
  10. f1=boost::ref(s); // This still works
  11. f1();
  12. boost::function0<void> f2;
  13. f2=boost::cref(s); // This doesn't work;
  14. // the function call operator is not const
  15. f2();

如果一个 function 包含一个被 boost::reference_wrapper 所包装的函数对象,那么复制构造函数与赋值操作就会复制该引用,即 function 的拷贝将引向原先的函数对象。

[cpp] view plaincopy
  1. int main() {
  2. keeping_state ks;
  3. boost::function1<int,int> f1;  // 译注:原文为boost::function<int,int> f1,有误
  4. f1=boost::ref(ks);
  5. boost::function1<int,int> f2(f1);  // 译注:原文为boost::function<int,int> f2(f1),有误
  6. boost::function1<short,short> f3;  // 译注:原文为boost::function<short,short> f3,有误
  7. f3=f1;
  8. std::cout << "The current total is " << f1(10) << '/n';
  9. std::cout << "The current total is " << f2(10) << '/n';
  10. std::cout << "The current total is " << f3(10) << '/n';
  11. std::cout << "After adding 10 three times, the total is "
  12. << ks.total() << '/n';
  13. }

这等同于使用 boost::ref 并把函数对象 ks 赋给每一个 function 实例。

给回调函数增加状态,可以发挥巨大的能力,这也正是使用 Boost.Function 与使用函数对象相比具有的非常突出的优点。

【Boost】boost库中function的用法相关推荐

  1. php function函数用法,js的function函数是什么?js中function的用法

    本篇文章给大家带来的内容是关于js的function函数是什么?js中function的用法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. Function与函数 Function是 ...

  2. Numpy库中einsum函数用法

    Numpy中einsum函数用法 一.一维张量收缩 二.二维张量收缩 2.1 收缩到零维张量 2.2 收缩到一维张量 三.三维张量收缩(重难点) 3.1 例1 3.2 例2 四.其他功能介绍(次要) ...

  3. 【Boost】boost库中function和bind一起使用的技巧(二)

    与 Boost.Function 一起使用 Boost.Bind 当我们把 Boost.Function 与某个支持参数绑定的库结合起来使用时,事情变得更为有趣.Boost.Bind 为普通函数.成员 ...

  4. 【Boost】boost库中function和bind一起使用的技巧(一)

    1 bind/function 引 (1)头文件 bind函数#include <boost/bind.hpp> function使用头文件#include <boost/funct ...

  5. 【Boost】boost库中bind的用法

    头文件: boost/bind.hpp bind 是一组重载的函数模板. 用来向一个函数(或函数对象)绑定某些参数.  bind的返回值是一个函数对象. 它的源文件太长了. 看不下去. 这里只记下它的 ...

  6. Boost C++ 库

    http://zh.highscore.de/cpp/boost/frontpage.html Boost C++ 库 目录 第 1 章 简介 第 2 章 智能指针 第 3 章 函数对象 第 4 章  ...

  7. linux怎么删除代码库,是否有从代码库中删除第三方C和C库的好技巧或工具? (OS X或Linux)...

    我正在减少和隔离我对某些库的使用.我编写的许多现有程序直接使用这些库.我想要编译器(在这种情况下是GCC和/或Clang)或一些工具来帮助我识别我的代码库中的这些用法.简而言之,我想在代码库中毒害这些 ...

  8. 使用 Boost.Lambda 库创建并存储在 Boost.Function 对象中的函子中使用 Boost.Units 的数量、单位和绝对类型

    使用 Boost.Lambda 库创建并存储在 Boost.Function 对象中的函子中使用 Boost.Units 的数量.单位和绝对类型 实现功能 C++实现代码 实现功能 使用 Boost. ...

  9. Boost库之function的使用

    http://www.cnblogs.com/hujian/archive/2009/06/04/1495813.html Boost库的function是一组函数对象包装类的模板,实现了一个泛型的回 ...

最新文章

  1. 关于2D互动技术的一些要点
  2. 无法解决 equal to 操作中 SQL_Latin1_General_CP1_CI_AS 和 Chinese_PRC_CI_AS 之间的排序规则冲突。...
  3. 可能是基于 Hooks 和 Typescript 最好的状态管理工具
  4. 关联规则算法php,科学网—加权关联规则权值的计算 - 叶文菁的博文
  5. C语言 十进制整数字符串转十六进制字符串
  6. 12.Linux之输入子系统分析(详解)
  7. LeetCode 1744. 你能在你最喜欢的那天吃到你最喜欢的糖果吗?(前缀和)
  8. 图像的抽线、抽丝、抽图 神马是alpha通道
  9. Mac动态壁纸应用Dynamic Wallpaper 11
  10. Whatsns_V6.03互亿无线短信插件安装说明
  11. html 左侧居中对齐,HTML的居中对齐
  12. Codeforces Round #612 (Div. 1) A. Garland(dp动态规划)
  13. “无边框”引发口水大战 供应链考验手机硬件创新
  14. 字节跳动否认完成支付牌照收购,但金融野心一直有
  15. APP性能测试_帧率测试
  16. sybase监控执行sql(转自新浪)
  17. 大数据笔记8—java基础篇4(面向对象-封装-继承-多态)
  18. 算法岗面经总结(京东)
  19. 以太坊智能合约 —— 最佳安全开发指南
  20. Serializable接口和Parcelable接口

热门文章

  1. Ant Design Pro入门之简介
  2. RequestBody注解
  3. Request_请求转发
  4. 其他类型的链表和线性表的总结(一级)
  5. basequickadapter详解_BaseRecyclerViewAdapter(持续更新!)
  6. MySQL 下载与配置教程(免安装版)
  7. ubuntu知道文件名查找文件路径
  8. timeit统计运行时间
  9. React 教程第六篇 —— 样式绑定
  10. 持续集成接口自动化-jmeter+ant+jenkins(一)