销毁对象后使用其引用:为何成员函数仍可正常调用?
为什么销毁后的对象成员函数还能正常调用?
这是典型的未定义行为场景——你的代码已经违反了C++的规则,只是刚好没触发崩溃而已,咱们一步步拆解来理解:
先定位代码的核心问题
在createA()函数里,局部对象A b的作用域仅限于内部的代码块{}。当这个代码块执行结束时,b会被自动销毁(也就是你看到输出里的des),但你却返回了它的引用*a(a指向的就是b的地址)。这就导致main()里的A &b变成了悬挂引用——它指向的对象已经彻底不存在了。
为什么b.f()还能正常执行?
关键在于你的f()成员函数完全没有访问对象的成员变量。
对于非虚成员函数,C++编译器的调用逻辑是这样的:
- 调用
obj.f()时,编译器会把它转换成类似A::f(&obj)的形式,把对象的地址作为隐含的this指针传递给函数。 - 你的
f()只是输出固定字符串,完全不需要使用this指针去读取对象的任何状态。所以哪怕this指向的是一块已经被释放的内存,只要这块内存还没被其他数据覆盖,函数依然能正常执行——因为它不需要依赖对象的任何成员数据。
如果把f()改成下面这样,你大概率会看到崩溃或者乱码:
// 先给类A加一个成员变量x int x = 10; void f() { cout << "bla: " << x << endl; }
这时候函数需要访问对象的成员,就会去读取this指向的无效内存,立刻触发错误。
关于未定义行为的重要提醒
一定要记住:未定义行为不代表一定会崩溃,C++标准对这种情况没有任何保证。你的代码在当前编译器/环境下看起来正常,换个编译器、换个系统,或者只是代码稍微调整一下,就可能出现崩溃、输出乱码,甚至更诡异的问题。
修正方案推荐
有两种常见的正确做法:
- 返回对象值而非引用:函数会返回一个新的对象(调用拷贝构造函数),完全避免悬挂问题:
A createA() { A b; return b; } - 用智能指针管理动态对象:避免手动管理内存的麻烦,同时保证对象生命周期可控:
#include <memory> std::unique_ptr<A> createA() { return std::make_unique<A>(); }
你的程序输出:
con des 0058FE0B 0058FE0B bla Press any key to continue . . .
两次输出的地址相同,是因为那块内存还没被系统重新分配,所以悬挂引用的地址看起来和原对象一致,但内存里的对象已经不存在了。
内容的提问来源于stack exchange,提问作者DasOhmoff San
相关产品推荐
相关产品推荐

