当对象绑定到成员函数时,为什么std :: function会调用析构函数?

时间:2013-11-08 12:37:28

标签: c++ function c++11 bind

#include <functional>
#include <iostream>

using namespace std;

class test {
public:
  test(){ p = new int[10];}
 void print_info(double a)
  {
    cerr << a << endl;
  }
  ~test(){
    cerr << __LINE__ << endl;
    delete []p;
  }
private:
  int *p;
};

int main()
{
    test t;
    std::function<void(void)> f = std::bind(&test::print_info, t, 2.0);
    //std::function<void(void)> f = std::bind(&test::print_info, std::cref(t), 2.0);
    return 0;
}

它会崩溃,因为test::~test()被调用两次。但是,如果我将t替换为std::cref(t)(或std::ref(t), &t),则在退出main()时只会调用~test()一次。

我没弄清楚原因。我在Ubuntu 12.04 64bit上,使用gcc 4.6.3。

3 个答案:

答案 0 :(得分:8)

您正在绑定对象t的副本。由于您的类尝试管理动态内存,但不遵循Rule of Three,因此它具有无效的复制语义 - 两个副本都会尝试在销毁时删除相同的动态数组。

如果您使用类似std::vector的类(具有有效的复制语义)来管理动态数组,那么一切都会没问题。

答案 1 :(得分:4)

由于你绑定了一个copy of object t,而不是t本身,当从bind对象返回时将会被破坏,因为你没有超载copy c-tor,默认复制c-tor将进行浅层复制,您的p将被删除两次。对于绑定t本身,您应该使用std::ref

答案 2 :(得分:0)

std::function<void(void)> f = std::bind(&test::print_info, t, 2.0);
  

它会崩溃,因为test :: ~test()被调用两次。但是,如果我用std :: cref(t)(或std :: ref(t),&amp; t)替换t,那么在退出main()时,只会调用一次~test()。

我没弄清楚原因。我在Ubuntu 12.04 64bit上,使用gcc 4.6.3。

原因实际上非常简单,std::bind函数创建了一个包含参数副本的仿函数对象。在你的程序中有一个错误,复制该类型的对象最终会有两个对象,这些对象具有指向同一内存的指针,并且都在其析构函数中调用delete[]

绑定&t时,会复制对象(指针)的地址。没有两个单独的对象,只有一个对象和一个指针。类似地,实用程序std::refstd::cref引用包装器,即包装不同对象并提供类似引用的语义的对象。无论复制std::ref多少次,所有副本都表现为对同一对象的引用。永远不会复制对象本身。