最近使用boost::bind比较多,发现有一些奇怪的现象。就是在用boost::bind传递引用的时候,如果引用的对象被释放,那么之后的函数执行必然会访问无效的地址儿出错。但是这个担忧是多余的,boost::bind在传递引用的时候,并不是真的传递引用,而是执行对象的拷贝构造函数重新生成了一个全新的对象,测试代码如下:

#include<iostream>
  #include <boost/bind.hpp>
  #include <boost/function.hpp>
  using namespace std;
  
  class A
  {
      public:
          A(){ i = 0; cout << "A constructor" << endl;}
          A(const A& a)
          {
              i = 0;
          cout << "A copay constructor" << endl;
          }
          int i;
  };
  
  typedef boost::function<void (void)> fun;
  
  void myfun(A& a)
  {
      a.i += 10;           
  }
  
int main(int argc, char** argv)
  {
      A a;    
      a.i = 0;
      fun f(boost::bind(myfun, a));
      f();    
      cout << a.i << endl;//此处输出0,而不是期望的10
      return 0;
  }

输出结果如下:

A constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
A copay constructor
0
可以看到,A类的构造函数被调用了很多次,这个我也不是很清楚内部的实现逻辑,但是可以清楚的是引用传递没有生效,实际上是生成了一个新的对象。再对新的对象的i进行加10,所以打印出的a对象的i实际上并没有被改变,为0;

这个设计我猜想boost设计者的原意也是担心引用对象被释放,造成引用无效对象而崩溃的问题,因此在传递引用的时候实际上是值传递。

如果真的想传递引用,也是有办法的,boost设计者给了boost::ref这个对象,明确表示我只传递引用,不要拷贝。以上代码可以改成:

fun f(boost::bind(myfun, boost::ref(a)));

更改之后输出为:

A constructor
10

这个就不会重新生成很多对象了,从始至终都只有一个对象

如果明确了引用对象不会被释放,建议使用boost::ref来强制避免拷贝,毕竟那么多对象的构造和析构都是对性能的浪费,也增加了出错的可能性。

Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐