自定义C++智能指针计数异常:仅打印时指针值才正确的问题排查
自定义智能指针引用计数异常问题分析
问题重现
实现带引用计数的smart_pointer类时出现诡异的异常现象:
类实现代码
#pragma once #include <iostream> template <typename T> class smart_pointer{ T* pointer; int* cnt; public: smart_pointer<T>(T *el): pointer(el) { int i = 1; cnt = &i; }; smart_pointer<T>(const smart_pointer<T>& other): pointer(other.pointer) { // std::cout << ", *(other.cnt): " << *(other.cnt); cnt = other.cnt; (*cnt)++; } // 拷贝构造函数 int counter(){ int c = *cnt; return c; } };
测试代码
int main(){ std::string h("hello"); smart_pointer<std::string> p(&h); std::cout << "p: " << p.counter(); smart_pointer<std::string> q(p); std::cout << ", q: " << q.counter() << std::endl; return 0; }
异常现象
- 未添加
std::cout时,输出p: 1, q: 6487781,引用计数完全错误 - 在拷贝构造函数中添加
std::cout << ", *(other.cnt): " << *(other.cnt);后,输出变为p: 1, *(other.cnt): 1, q: 2,看似正常 - 仅执行
*(other.cnt)不打印,问题依旧存在
原因拆解
核心问题:局部变量内存非法访问
在smart_pointer的构造函数里,int i = 1; cnt = &i;是致命错误:i是构造函数内的局部栈变量,当构造函数执行完毕,这个变量的生命周期直接结束,对应的栈内存会被系统回收或重新分配。此时cnt指向的是一块已经失效的内存地址,后续对*cnt的读写操作都属于未定义行为——程序可能输出随机值、崩溃,或者看起来“正常”,完全取决于内存的复用情况。为什么加cout后看似正常?
当你在拷贝构造函数中添加std::cout打印*(other.cnt)时,cout的内部操作会占用栈空间,刚好没有覆盖之前i所在的那块内存区域,导致那块内存暂时还保留着原来的1值,所以后续的(*cnt)++能得到正确的2。但这只是巧合,不是真正的修复——换个编译器、调整代码顺序或者优化级别,结果可能又会异常。仅执行
*(other.cnt)不打印为什么没用?
现代编译器会做死代码消除优化,如果你的代码里只是读取*(other.cnt)但没有使用这个值,编译器会直接把这行代码删掉,相当于没执行。所以内存里的值还是没被“保留”,依旧是未定义的随机值。
正确修复思路
引用计数的内存必须在堆上分配,确保生命周期不受构造函数影响:
smart_pointer<T>(T *el): pointer(el) { cnt = new int(1); // 堆上分配计数变量 }; // 补充析构函数,当计数归0时释放内存和指针 ~smart_pointer() { if (--(*cnt) == 0) { delete pointer; delete cnt; } }
内容的提问来源于stack exchange,提问作者Marc Miller
相关产品推荐
相关产品推荐

