我认为这个问题最好用我刚写的一个小代码片段提出:
#include <iostream>
using namespace std;
class BasicClass
{
public:
BasicClass()
{
}
void print()
{
cout << "I'm printing" << endl;
}
};
class FriendlyClass
{
public:
FriendlyClass(BasicClass& myFriend) :
_myFriend(myFriend)
{
}
void printFriend()
{
cout << "Printing my friend: ";
_myFriend.print();
}
private:
BasicClass& _myFriend;
};
int main(int argv, char** argc)
{
FriendlyClass* fc;
{
BasicClass bc;
fc = new FriendlyClass(bc);
fc->printFriend();
}
fc->printFriend();
delete fc;
return 0;
}
代码使用g ++编译并运行良好:
$ g++ test.cc -o test
$ ./test
Printing my friend: I'm printing
Printing my friend: I'm printing
但是,这不是我期待的行为。第二次拨打fc->printFriend()
时,我期待某种失败。我对通过引用传递/存储如何工作的理解是不正确的还是这种情况恰好在小规模上工作并且可能在更复杂的应用程序中爆炸?
答案 0 :(得分:12)
当您存储对已终止其生命周期的对象的引用时,访问它是未定义的行为。所以任何事情都可能发生,它可以发挥作用,它可能会失败,它可能会崩溃,而且我觉得它可以说它可以订购披萨。
答案 1 :(得分:11)
它与指针完全相同:使用引用不再存在的对象的某些东西(指针/引用)是未定义的行为。它似乎可以工作,但它可以随时中断。
警告:下面简要说明为什么这样的方法调用似乎可以在多个场合工作,仅用于提供信息;在编写实际代码时,您应该只依赖标准所说的
对于您正在观察的行为:在大多数(所有?)编译器上,方法调用实现为具有隐藏this
参数的函数调用,该参数引用该方法将在其上运行的类的实例。但在你的情况下,this
指针根本没有被使用(函数中的代码不是指任何字段,也没有虚拟调度),所以(现在无效)this
指针未被使用,调用成功。
在其他情况下,它似乎可以工作,即使它指的是超出范围的对象,因为它的内存还没有被重用(虽然析构函数已经运行了,所以该方法可能会找到一个不一致的状态)。
同样,你不应该依赖这些信息,它只是让你知道为什么这个电话仍然有效。
答案 2 :(得分:1)
未定义的行为。根据定义,您无法假设代码运行时会发生什么。编译器可能没有清除bc
所在的内存,但你不能指望它。
我实际上在工作中的程序中修复了同样的错误。当使用英特尔的编译器时,超出范围的变量尚未“清理”,因此内存仍然“有效”(但行为未定义)。然而,微软的编译器更加积极地清理它,而且这个bug很明显。
答案 3 :(得分:1)
你有一个悬空引用,导致未定义的行为。
答案 4 :(得分:1)
查看此处:Can a local variable's memory be accessed outside its scope?
它几乎可以工作,因为你没有虚函数,并且你不访问BasicClass的字段:你调用的所有方法都有静态绑定,并且从不使用'this', 所以你永远不会真正访问“未分配内存”。