为何绑定后的类成员函数在对象销毁后仍可执行?
为何绑定后的类成员函数在对象销毁后仍可执行?
嘿,这个问题其实是踩了C++里未定义行为的坑,咱们掰开揉碎了说~
你是不是写了类似这样的代码?(我把你没写完的示例补全了)
#include <functional> #include <iostream> #include <string> using namespace std; class foo { public: string name; foo(const string& name) : name(name) {} ~foo() { cout << "Destroyed: " << name << endl; } void print() { cout << "Name: " << name << endl; } }; int main() { function<void()> f; { foo obj("test"); f = bind(&foo::print, &obj); // 绑定栈上对象的地址 } // 这里obj已经被销毁,栈空间被回收 f(); // 居然还能输出? return 0; }
首先得明确:这种“能执行”完全是碰运气,本质是未定义行为,不是真的“合法工作”。
为啥会出现这种情况?
- 当你用
std::bind绑定成员函数时,如果你传的是对象的指针或引用,bind只会存下这个指针/引用,不会帮你复制整个对象。 - 当对象被销毁后,这个指针就变成了野指针,引用也成了悬垂引用。这时候调用绑定的函数,相当于拿着无效的地址去访问内存——运气好的话,原来的内存还没被新数据覆盖,你就能读到之前的
name;运气差的话,直接崩溃、输出乱码,甚至做任何离谱的事(因为未定义行为的结果是C++标准完全不约束的)。
怎么避免这个坑?
给你两个靠谱的解决思路:
- 绑定对象副本:如果对象不大,绑定的时候直接传对象本身(值传递),比如
f = bind(&foo::print, obj)。这时候bind内部会复制一个foo对象,原对象销毁后,副本还活着,调用就完全合法了。 - 用智能指针管理生命周期:把对象放在
std::shared_ptr里,绑定的时候传这个智能指针,比如auto obj = make_shared<foo>("test"); f = bind(&foo::print, obj);。这样bind会持有一个shared_ptr的副本,只要f还存在,对象就不会被销毁,从根源上避免悬垂引用。
总之,别被“看起来能运行”迷惑了,这种未定义行为绝对不能依赖,严格管理对象的生命周期才是正道~
内容来源于stack exchange
相关产品推荐
相关产品推荐

