这是一些C ++代码。
#include <iostream>
using namespace std;
class test{
int a;
public:
test(int b){
a = b;
cout << "test constructed with data " << b << endl;
}
void print(){
cout << "printing test: " << a << endl;
}
};
test * foo(){
test x(5);
return &x;
}
int main()
{
test* y = foo();
y->print();
return 0;
}
这是它的输出:
test constructed with data 5
printing test: 5
我的问题:为什么指向x的指针仍然在函数foo的上下文之外“工作”?据我所知,函数foo创建了一个test实例并返回该对象的地址。
函数退出后,变量x超出范围。我知道C ++不是垃圾收集 - 变量超出范围时会发生什么?为什么在foo()中返回的地址仍指向看似有效的对象?
如果我在某个范围内创建一个对象,并希望在另一个范围内使用它,我应该在堆中分配它并返回指针吗?如果是这样,我何时/何地将其删除
由于
答案 0 :(得分:8)
x
是一个局部变量。 foo
返回后,无法保证x
所在堆栈中的内存损坏或完好无损。这是未定义行为的本质。在阅读x
之前运行一个函数,你会发现引用“死”变量的危险:
void nonsense(void)
{
int arr[1000] = {0};
}
int main()
{
test* y = foo();
nonsense();
y->print();
return 0;
}
我机器上的输出:
用数据5构建的测试 印刷测试:0
答案 1 :(得分:3)
当变量超出范围时,将调用析构函数(对于非POD数据),并且该变量占用的位置现在被视为未分配,但实际上并未写入内存,因此旧值仍然存在。这并不意味着您仍然可以安全地访问此值,因为它位于标记为“空闲”的位置。新变量可以驻留,也可以在此内存空间中进行分配。
内存未被擦除的原因是因为你实际上无法擦除内存,你可以做的就是像所有零或全部或随机一样写一些内容,这不仅没有意义,而且性能下降。
它与垃圾收集无关。垃圾收集器不会“擦除”内存,但会将其标记为空闲。您所描述的行为存在于C而不是Java中的原因不是垃圾收集器,而是C允许您通过指针访问任何您想要,分配或不分配,有效或无效的内存,而Java不会t(公平地说,垃圾收集器是Java可以制作的原因,因此您无法访问任何内存)。
可以对删除文件时磁盘上发生的事情进行类比。文件内容保留(它们不会被覆盖),而是修改文件系统中的指针(句柄),以便磁盘上的内存被认为是空闲的。这就是为什么特殊工具可以恢复已删除的文件:信息仍然存在,直到有新的内容写入它,如果你可以指向它,你就可以获得它。与C中的指针几乎相同。想想每次删除4GB文件时在磁盘上实际写入4GB是什么意思。没有必要在内存中为超出范围的每个变量写入该变量的整个大小。你只需将其标记为免费。