如果我正确地解释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();
}
放入_k
实例变量来检查堆栈帧是否存在。
令人惊讶的是,它不是段错误而是正确打印'10',而我认为A
在堆栈上分配,f()
的堆栈帧将被至少cout<<
覆盖调用
答案 0 :(得分:27)
这是未定义的行为,您很幸运,a
的内存尚未用于其他任何内容。在更复杂的情况下,你几乎肯定会得到垃圾。在我的机器上,我使用此代码获得随机垃圾。对我来说,这可能是因为我使用的是64位机器,它使用寄存器调用约定。寄存器比主存储器更频繁地重复使用(理想情况下......)。
所以回答你的“发生了什么”的问题。那么在这种情况下,引用可能只是一个有限指针和更友好的语法:-)。在引擎盖下,存储a
的地址。稍后a
对象超出了范围,但B
对象对a
的引用不会“自动神奇地”更新以反映这一点。因此,您现在有一个无效的引用。
使用这个无效的引用几乎可以产生任何事情,有时候会崩溃,有时只会产生双层数据。
编辑:感谢Omnifarious,我一直在考虑这个问题。 c ++中有一条规则,基本上说如果你有一个临时的const引用,那么临时的生命周期至少和const引用一样长。这引入了一个新问题。
编辑:针对感兴趣的人(const reference to temporary oddity)移动到单独的问题
答案 1 :(得分:6)
在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
P.S。您错误地使用了术语范围。 范围是标识符的可见区域。范围本身与对象生命周期无关。当某些东西“留下范围”时,通常情况下某些东西不会被破坏。使用“叶子范围”措辞来引用对象的破坏点是一种流行的误解。