【问题标题】:The meaning of static in C++C++中static的含义
【发布时间】:2011-05-08 08:17:43
【问题描述】:

我以为我的 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

所以,我认为 static 正在默默地更新 x 失败,而第二个 getX 正在使用(在删除之后)未初始化的内存;为了测试这一点,我添加了 x = 0;在 delete 之后,new 之前,以及另一个 cout 以确保 x 确实重置为 0。它是。

那么,这里发生了什么?为什么 new 返回的内存块与之前的 delete 应该释放的内存块完全相同?这仅仅是因为这是操作系统的内存管理器决定做的事情,还是我缺少静态的一些特别之处?

谢谢!

【问题讨论】:

标签: c++ memory-management static


【解决方案1】:

这正是内存管理器决定做的事情。如果你仔细想想,这很有意义:你刚刚释放了一个 int,然后你又请求了一个 int……为什么内存管理器不应该把你刚刚释放的 int 还给你?

从技术上讲,当您delete 时可能发生的事情是内存管理器将您释放的内存块附加到空闲列表的开头。然后,当您调用new 时,内存管理器会扫描其空闲列表并在第一个条目处找到大小合适的块。

有关动态内存分配的更多信息,请参阅"Inside storage allocation"

【讨论】:

  • 是的。我尝试在delete x; 之后添加int * MyPerturbation = new int;,并在x = new int; 之后添加delete MyPerturbation;:输出不同。
  • @Martin B:事后看来,“为什么不应该......”很明显。出于某种奇怪的原因,我专注于 static 关键字。你的回答很有道理。
  • @wok:很好的测试。我应该在发布之前就这样做,并为大家省去了一些麻烦:)。谢谢!
  • 他的回答不正确。那就是 UB,任何事情都可能发生(包括鼻恶魔)。
  • @VJo:问题不在于访问是否是 UB,问题是为什么块的地址在删除之前和新建之后都相同。我认为 Martin B 完美地回答了这个问题。我赶紧补充一下,那显然,你是对的,上面是UB。不过,这个问题的目的并不是要找出答案。
【解决方案2】:

关于你的第一个问题:

X:100,X:200。我不明白为什么。

由于Y.my_x 只是对GenX 中的static *x 的引用,因此这正是它应该的样子——两者都引用内存中的相同地址,并且当您更改 *x 的内容时,你会得到一个副作用。

【讨论】:

  • 问题是真的为什么地址没有改变,即使在新/删除之后。但是,我可能说得不够清楚。
  • 您的帖子下方有一个“编辑”按钮,可以随时改进您的问题。编辑器上方有“代码”符号,可帮助您正确格式化代码示例;-)
  • 这个答案并不完全正确,或者至少,它告诉我的事情与发生的事情不同。 Y.my_x 是在特定时间对对象*x 的引用,但这只是一个实现怪癖,x 再次获取相同的值以便在正确的位置构造一个对象以使 Y.my_x 仍然有效。
【解决方案3】:

您正在访问已释放的内存块。根据 c++ 标准,这是一种未定义的行为,因此任何事情都可能发生。

编辑

我想我必须画画:

  1. 您为 int 分配内存,并将在堆上分配的对象传递给 Y 的构造函数,后者将其存储在引用中
  2. 然后您释放了该内存,但您的对象 Y 仍然持有对已释放对象的引用
  3. 然后您再次访问 Y 对象,该对象包含一个无效引用,引用已释放的对象,而您得到的结果是未定义行为的结果。

EDIT2

原因的答案:实现定义。编译器可以在它喜欢的任何位置创建新对象。

【讨论】:

  • 从技术上讲,你不是。新的返回与删除销毁相同的旧内存块。但是,是的,这显然会改变并且是特定于实现的。我只是对这里的“为什么”感到好奇。感谢您的回复!
【解决方案4】:

我在 VC2008 中测试你的代码,输出是 X: 100, X: -17221323。我认为原因是静态 x 被释放了。我认为我的测试是合理的。

【讨论】:

  • 你的测试是合理的,OP得到的结果非常依赖他的实现。
【解决方案5】:

这对代码来说非常有意义。

记住,你的指针是静态的,所以当你第二次进入这个函数时,你不需要创建一个新的指针,但是每次你进入这个函数时,你都会为指向的指针创建一个新的 int到。

您也可能处于调试模式,需要更多时间为您提供漂亮的地址。

究竟为什么你的指针指向的 int 在同一个空间中可能只是纯粹的运气,而且你没有在它之前声明任何其他变量,所以内存中的相同空间仍然是空闲的

【讨论】:

    猜你喜欢
    • 2020-09-29
    • 2014-10-07
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    • 2019-02-07
    相关资源
    最近更新 更多