当C++引用离开其范围时会发生什么?

whi*_*ark 21 c++

如果我正确地解释C++引用,它们就像指针一样,但保证数据完整性(没有NULL,没有(int*)0x12345).但是当引用对象的范围被遗漏时会发生什么?如果没有涉及魔法(可能不是),引用的对象将在我背后被摧毁.

我写了一段代码来检查:

#include <iostream>
using namespace std;

class A {
public:
        A(int k) { _k = k; };
        int get() { return _k; };
        int _k;
};

class B {
public:
        B(A& a) : _a(a) {}
        void b() { cout << _a.get(); }
        A& _a;
};

B* f() {
        A a(10);
        return new B(a);
}

int main() {
        f()->b();
}
Run Code Online (Sandbox Code Playgroud)

_k实例变量放入检查堆栈帧的存在.

令人惊讶的是,它不是段错误而是正确地打印'10',而我认为它A是在堆栈上分配的,并且堆栈帧f()将被至少cout<<调用覆盖.

Eva*_*ran 29

这是未定义的行为,你只是幸运的是,内存a还没有被用于其他任何东西.在更复杂的情况下,你几乎肯定会得到垃圾.在我的机器上,我使用此代码获得随机垃圾.对我来说,这可能是因为我使用的是64位机器,它使用寄存器调用约定.寄存器比主存储器更频繁地重复使用(理想情况下......).

那么回答你的"发生了什么"的问题.那么在这种情况下,引用可能只是一个有限指针和更友好的语法:-).在引擎盖下a存储地址.稍后该a对象超出范围,但B对象对该对象的引用a将不会"自动神奇地"更新以反映这一点.因此,您现在有一个无效的引用.

使用这个无效的引用几乎可以产生任何东西,有时候会崩溃,有时只会产生双层数据.


编辑:感谢Omnifarious,我一直在想这个.c ++中有一条规则,基本上说如果你有一个临时的const引用,那么临时的生命周期至少和const引用一样长.这引入了一个新问题.

编辑:移动到感兴趣的人的单独问题(const引用临时奇怪)

  • @whitequark:编译器类在某些有限的情况下进行编译时引用计数.如果你做了类似'A&x = A(10);`的话,你可以保证`x`引用的`A`对象在`x`超出范围之前不会被销毁.只有当`x`具有块范围时才有效.如果`x`是成员变量或(我认为)是全局变量或静态变量,它将无法工作. (4认同)

AnT*_*AnT 8

在C++语言中,引用所绑定的对象的生命周期与引用本身的生命周期没有任何关系,只有一个例外(见下文).

如果在引用之前销毁对象,则引用变为无效(如悬挂指针).任何访问该引用的尝试都会导致未定义的行为.这是您的示例中发生的情况.

引用在对象之前结束它的生命周期,然后......好吧,没有任何反应.引用消失,对象继续存在.

我上面讨论的唯一例外是使用直接临时对象初始化const引用.在这种情况下,临时对象的生命周期与引用的生命周期相关联.当参考死亡时,对象随之死亡

{
   const std::string& str = "Hello!"; 
   // A temporary `std::string` object is created here...
   ...
   // ... and it lives as long as the reference lives
   ...
} // ... and it dies here, together with the reference
Run Code Online (Sandbox Code Playgroud)

PS您正在使用术语范围错误.范围是标识符的可见区域.范围本身与对象生命周期无关.当某些东西"留下范围"时,一般情况下某些东西不会被破坏.使用"叶子范围"措辞来指代对象的破坏点是一种流行的误解.

  • 感谢您指出 _scope_ 误用——有时试图将一种语言中的术语映射到另一种语言会产生非常奇怪的结果——但这整个问题是关于当堆栈分配的对象在其标识符超出范围时被销毁时发生的事情。(是的,内存只在 `leave` 时被释放,但无论如何都应该调用析构函数,因为它被销毁了。) (2认同)