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

C++程序中string的const引用超出作用域后仍有效是什么原因?

问题现象说明

存在如下代码逻辑:

#include <iostream>
#include <string>
#include <memory>

using namespace std;

struct Def
{
    const string& s;
    Def(const string& v):s(v)
    {
    }
};
int main()
{
    unique_ptr<Def> d = nullptr;
    {
        string cst = "Hello";
        d.reset(new Def(cst));
        std::cout <<&d->s <<"="<< &cst<<"\n";
    }
    std::cout << "strVal : " << d->s<<  "\n";

    return 0;
}

按照C++基础语法规则,块作用域内创建的string cst会在块结束时销毁,存储了其const引用的unique_ptr<Def> d的成员s应该变成悬垂引用,但运行时却正确打印了"Hello"字符串,且打印的两个地址完全一致,没有发生拷贝。


原因解答
  • 首先必须明确:该代码属于未定义行为,"运行正常"只是未定义行为的偶发表现,不代表代码逻辑合法,在不同编译参数、编译器版本、运行环境下完全可能出现乱码、程序崩溃等异常结果。
  • 块作用域结束时,cst作为自动变量确实会被销毁,其占用的栈内存会被编译器标记为可复用,但是运行时不会主动擦除这块内存中存储的原有内容。你的示例代码逻辑非常简单,在块结束到打印d->s之间没有任何操作占用这块被释放的栈内存,所以原有的"Hello"字符串内容暂时保留在了原地,读取时自然就拿到了旧值。
  • 该现象也和std::string的短字符串优化(SSO)特性相关:大部分主流标准库的std::string实现都会把短于特定长度(通常为15/23字节)的字符串直接存储在std::string对象本身的栈空间中,不需要额外申请堆内存存储。所以cst销毁时只要对应的栈内存未被覆盖,就能读到完整的字符串内容,如果换成超过SSO阈值的长字符串,对应的堆内存会在cst销毁时被回收,读取出现异常的概率会高很多。
  • 你可以自行修改代码验证:在块结束后、打印d->s之前新增局部变量、或者调用任意存在局部变量的函数覆盖这块栈内存,再打印的结果大概率就是乱码了。
  • 额外说明:C++中const引用延长生命周期的规则仅适用于绑定临时对象的场景,你这里引用绑定的是已经存在的左值cst,不触发生命周期延长规则,所以引用确实是悬垂状态。

内容的提问来源于stack exchange,提问作者CY5

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 03:06:04