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

含虚函数类右值的const引用调用引发异常的原理与相关疑问

右值绑定到const引用后的生命周期与虚函数调用问题

原错误代码

class A
{
public:
    virtual uint32_t getNumber() const
    {
        return 0;
    };
};

class B
{
public:
    B( const A& a )
        : a_( a )
    {}
    const A& a_;
    void test()
    {
        a_.getNumber();  // 调用时触发异常
    }
};
B b{ A{} };

int main()
{
    b.test();
    return 0;
}

现象说明

使用MSVC编译器编译执行上述代码时,调用a_.getNumber()会抛出异常;将getNumber()改为非虚函数后,代码可正常运行:

class A
{
public:
    uint32_t getNumber() const
    {
        return 0;
    };
};

一、问题背后的核心机制

  1. 悬垂引用的产生:B b{ A{} };创建了一个临时A对象(右值),它被绑定到B构造函数的const引用参数a。但B类的成员a_只是复制了这个引用,并没有直接绑定到临时对象。根据C++规则,临时对象的生命周期仅被延长至直接绑定的引用的作用域结束——也就是B构造函数执行完毕后,临时A对象就会被销毁,b.a_随即变成悬垂引用。
  2. 虚函数与非虚函数的差异:
    • 调用虚函数时,需要通过对象实例中的虚表指针(vptr)查找虚表(vtable)来确定函数地址。临时对象销毁后,a_指向的内存已无效,访问vptr会触发未定义行为(MSVC表现为抛出异常)。
    • 非虚函数是静态绑定,编译期就确定了函数地址,调用时不需要访问对象的任何内存(本例中函数仅返回固定值),所以即使引用悬垂,也可能“正常运行”——但这本质仍是未定义行为,只是巧合没有触发错误。

二、右值的const引用有效期规则

C++标准明确:

  • 当右值直接绑定到const左值引用时,临时对象的生命周期会被延长至该引用的生命周期结束,比如const A& ref = A{};,临时A会和ref共存亡。
  • 若引用是间接绑定(比如通过另一个引用传递、赋值),则生命周期不会被延长,临时对象会在其原始作用域结束时销毁(本例中就是B构造函数执行完毕时)。

三、不同编译器的处理差异

这种场景属于未定义行为,C++标准没有强制规定程序的表现,因此不同编译器的处理可能完全不同:

  • MSVC会检测到悬垂引用访问虚表的非法操作,抛出异常。
  • 部分编译器在特定优化级别下,可能因为临时对象的内存未被立即覆盖、非虚函数被内联等原因,让程序看似正常运行,但这完全是偶然情况,绝对不能依赖。

内容的提问来源于stack exchange,提问作者Oliver S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 02:15:37