我以为我对C相当不错,事实证明我不是 . 我问过上一个问题:C++ const lvalue references在其中一个答案中有以下代码:
#include <iostream>
using namespace std;
int& GenX(bool reset)
{
static int* x = new int;
*x = 100;
if (reset)
{
delete x;
x = new int;
*x = 200;
}
return *x;
}
class YStore
{
public:
YStore(int& x);
int& getX() { return my_x; }
private:
int& my_x;
};
YStore::YStore(int& x)
: my_x(x)
{
}
int main()
{
YStore Y(GenX(false));
cout << "X: " << Y.getX() << endl;
GenX(true); // side-effect in Y
cout << "X: " << Y.getX() << endl;
return 0;
}
上面的代码输出X:100,X:200 . 我不懂为什么 . 我玩了一下,并添加了一些输出,即删除x之前的cout;和新的x之后的cout;在复位控制块内 .
我得到的是:删除之前:0x92ee018之后:0x92ee018
所以,我认为静态无声地更新到x,第二个getX正在播放(删除后)未初始化的内存;为了测试这个,我添加了一个x = 0;在删除之后,在新的之前,另一个cout确保x确实被重置为0.这是 .
那么,这里发生了什么?为什么新的返回完全相同的内存块,以前的删除应该是免费的?这只是因为那是操作系统内存管理器决定做的事情,还是我缺少哪些关于静态的特殊内容?
谢谢!
5 回答
我在VC2008中测试你的代码,输出是X:100,X:-17221323 . 我认为原因是静态x被释放 . 我认为我的测试是合理的 .
这正是内存管理员决定做的事情 . 如果你考虑一下,它很有意义:你刚刚释放了一个int,然后你再次要求一个int ...为什么内存管理器不能给你回来你刚刚释放的int?
从技术上讲,当你在内存管理器附加你释放到空闲列表开头的内存块时,可能会发生什么 . 然后当你调用
new
时,内存管理器会扫描其空闲列表,并在第一个条目中找到一个合适大小的块 .有关动态内存分配的详细信息,请参阅"Inside storage allocation" .
至于你的第一个问题:
由于
Y.my_x
只是GenX
中static *x
的引用,这正是它应该如何 - 两者都引用了内存中的相同地址,当你更改* x的内容时,会产生副作用 .这对代码非常有意义 .
请记住,它是你的指针是静态的,所以当你第二次输入这个函数时你不需要创建一个新的指针,但是当你进入这个函数时,你正在为指针指向一个新的int .
您也可能处于调试模式,花费更多时间为您提供好的地址 .
究竟为什么你的指针指向的int在同一个空间中可能只是为了纯粹的运气,那你并没有在它之前声明任何其他变量,所以内存中的相同空间仍然是免费的
您正在访问已解除分配的内存块 . 根据c标准,这是一种未定义的行为,因此任何事情都可能发生 .
EDIT
我想我得画画:
为int分配内存,并将堆上分配的对象传递给Y的构造函数,该构造函数将其存储在引用中
然后您释放该内存,但您的对象Y仍然保留对释放的对象的引用
然后再次访问Y对象,该对象包含无效引用,引用已释放的对象,并且您获得的结果是未定义行为的结果 .
EDIT2
答案为什么:实现定义 . 编译器可以在任何喜欢的位置创建新对象 .