You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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)不打印,问题依旧存在

原因拆解

  1. 核心问题:局部变量内存非法访问
    在smart_pointer的构造函数里,int i = 1; cnt = &i;是致命错误:i是构造函数内的局部栈变量,当构造函数执行完毕,这个变量的生命周期直接结束,对应的栈内存会被系统回收或重新分配。此时cnt指向的是一块已经失效的内存地址,后续对*cnt的读写操作都属于未定义行为——程序可能输出随机值、崩溃,或者看起来“正常”,完全取决于内存的复用情况。

  2. 为什么加cout后看似正常?
    当你在拷贝构造函数中添加std::cout打印*(other.cnt)时,cout的内部操作会占用栈空间,刚好没有覆盖之前i所在的那块内存区域,导致那块内存暂时还保留着原来的1值,所以后续的(*cnt)++能得到正确的2。但这只是巧合,不是真正的修复——换个编译器、调整代码顺序或者优化级别,结果可能又会异常。

  3. 仅执行*(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 23:40:37