2011-06-21 31 views
2

我有一个关于为什么我可以访问某些内存块的问题,我认为它与我理解(或不理解)编译器如何将内存保存在内存中的方式有​​关。这是示例代码我的工作:C++清除的内存仍可访问

头文件:

#include <iostream> 
using namespace std; 

class A 
{ 
public: 
int value; 
A (int newValue = 5) 
{ 
    value = newValue; 
    cout << "A()" << endl; 
} 
~A() { cout <<"~A()" << endl; } 
void func1() {cout << "A::func1()" << endl; } 

}; 


class B : public A 
{ 
public: 
B() { A::value = 0; cout << "B()" << endl; } 
~B() { cout << "~B()" << endl; } 
virtual void func1() { cout << "B::func1()" << endl; } 

}; 

class C : public B 
{ 
public: 
C() { cout << "C()" << endl; } 
~C() { cout << "~C()" << endl; } 
virtual void func1() { cout << "C::func1()" << endl; } 
}; 

.cpp文件:

int main() 
{ 
int i; 

cout << endl; 
A* pA = new A (5); 
B* pB = new B; 
C* pC = new C; 

pA->func1(); 
pB->func1(); 
pC->func1(); 

delete pA; 
delete pB; 
delete pC; 

cout << endl; 

    //here is my first question - why don't I get a compiler error here? 
    //why does the compiler know what this is? Didn't I delete it? 
A* ppA = pC; 

    //here is my second question - why does this work? 
    //didn't I clear this memory? 
ppA->func1(); 
B bObject; 
B* ppB = &bObject; 
ppB->func1(); 


cin >> i; 

    return 0; 
} 

我的问题是正确的,在评论 - 为什么我没有得到这些线路上的错误?

如果我更改.h文件,使func1()virtual也在A中,我确实在该行上发生访问冲突,但仍然没有编译时错误。

感谢您的任何解释:)

+0

我相信这可能是因为你用delete删除了内存,但它可能没有写完。因此数据可能仍然存在。但是不确定。 –

+0

仅供参考_compiler_不会将内容保存在内存中。该任务由操作系统执行。 – ALOToverflow

+0

@Francis,啊,那些善良的迂腐人物,他们只是对他们迂腐的东西有一个模糊的概念。你知道G ++在编译一个文件的内存中“保存”了更多的东西,而不是所有的程序放在一起吗?只是“供参考”。 – Blindy

回答

7

第一个问题的答案很简单:指令是在编译时写入的。编译器显然不知道该值是否已被删除,所以它只是盲目地将pC的值复制到ppA以上。

第二个问题的答案是:它可能无法正常工作。你已经进入了可怕的undefined-behavior-land。想想爱丽丝梦游仙境,没有猫来引导你。

基本上,当您在指针上调用operator delete时,您告诉操作系统,如果需要可以覆盖内存。 “如果需要的话。没有实际的内存擦除正在进行,只是承诺您不会再使用该内存块。

因此,当您尝试访问虚拟表以调用func1时,数据可能仍然存在。或不。在这种情况下,你会发生崩溃(如果你幸运的话)。但是你应得的,你破坏了对操作系统的承诺,所以现在什么都行。

编辑:注意func1从未实际使用this,所以如果它不是虚拟的,你可能会说((A*)0)->func1(),它会工作得很好。

8

编译器不跟踪指针是否指向一个合法的对象。这是一个复杂的分析,在大多数情况下甚至不可能。这就是为什么当你将一个已删除的指针指定给另一个指针时它不会产生错误。

删除对象不会自动清除对象用来占用的内存。它将在未来的某个不确定时刻被覆盖,所以你不应该依赖它。这被称为未定义的行为,未定义行为的特点之一就是即使它不应该也可能工作。

1

delete不会从代码范围中删除名称。这是一个将该内存标记为不再被程序使用的命令。该内存可能会被下一次分配使用,或者在进程执行期间不再使用。

delete不会为您清零内存。如果你需要,你可以自己做。直到它决定在那里设置一个新分配时,那块内存中的内容才会被改变。使用删除内存的指针是一个非常糟糕的想法。

0

内存释放功能,如删除,免费,HeapFree不会消灭内存。他们会将它们标记为“未分配”,并且可以通过这些未分配的内存块完成进一步的请求。同一个内存块可以用来服务多个内存分配调用,或者只是一个调用。

这就像删除(Shift + Deleting)文件 - 该文件实际上不会被标记为零,它只会被标记为“空闲空间”。与文件分配一样,多个空闲空间可以用于一个文件(这就是为什么我们有“碎片整理程序”)。但是对于内存分配,服务于大内存块分配不能由多个空闲内存块完成。