析构函数调用位置解析及析构后成员函数可调用原因咨询
关于C++析构函数调用与成员函数的疑问解答
咱们先把你没写完的代码补全(大概率是通过sharedPtr.get()拿到了原始指针ent),完整的代码应该是这样的:
#include <iostream> #include <memory> class Entity { public: Entity() { std::cout << "Ctor Call" << std::endl; } ~Entity() { std::cout << "Destroy Call" << std::endl; } void Print() { std::cout << "Print me" << std::endl; } }; int main() { Entity* ent; { std::shared_ptr<Entity> sharedPtr = std::make_shared<Entity>(); ent = sharedPtr.get(); // 拿到智能指针管理的原始指针 } // 这里就是析构函数被调用的位置 ent->Print(); // 你疑问的:析构后调用成员函数 }
一、析构函数的调用位置
析构函数是在**内部代码块的结束处(也就是那个})**被触发的:
- 内部的
sharedPtr是局部代码块里定义的智能指针,当代码执行到这个块的结尾时,sharedPtr会离开它的作用域,触发自身的析构函数。 - 因为这个
sharedPtr是Entity对象的唯一持有者(引用计数从1降到0),所以它会自动调用所管理的Entity对象的析构函数,也就是这里会打印出Destroy Call。
二、为什么析构后还能调用Print()?
这其实是C++里的未定义行为,只是刚好你的Print()函数没用到对象的成员,所以看起来“正常运行”:
- 对象被析构后,它占用的内存并不会立刻被操作系统清空或者回收,只是被标记成可以被其他数据复用的状态。
Print()是一个非虚成员函数,编译器会把它转换成类似Entity_Print(ent)的调用形式,这里的ent就是对象的指针。如果函数里没有访问对象的任何成员变量(你的Print()只是输出固定字符串,根本没用到this指针指向的内容),那即使ent是指向已析构对象的野指针,函数依然能执行——因为它不需要读取对象内存里的数据。- 但这绝对不是合法操作!要是
Print()里访问了对象的成员变量,程序大概率会崩溃、输出乱码,或者出现各种不可预测的问题,毕竟这块内存已经不属于这个Entity对象了。
三、总结提醒
- 析构函数的触发时机就是
sharedPtr离开局部作用域的时候,因为它的引用计数归零了。 - 析构后调用成员函数能“运行”只是巧合,属于未定义行为,实际开发中千万不能这么写。正确的做法是始终用智能指针管理对象的生命周期,别随便保存智能指针返回的原始指针,更不能在智能指针失效后用它。
内容的提问来源于stack exchange,提问作者Yanshof
相关产品推荐
相关产品推荐

