I have written a simple, working tetris game with each block as an instance of a class singleblock.
class SingleBlock
{
public:
SingleBlock(int, int);
~SingleBlock();
int x;
int y;
SingleBlock *next;
};
class MultiBlock
{
public:
MultiBlock(int, int);
SingleBlock *c, *d, *e, *f;
};
SingleBlock::SingleBlock(int a, int b)
{
x = a;
y = b;
}
SingleBlock::~SingleBlock()
{
x = 222;
}
MultiBlock::MultiBlock(int a, int b)
{
c = new SingleBlock (a,b);
d = c->next = new SingleBlock (a+10,b);
e = d->next = new SingleBlock (a+20,b);
f = e->next = new SingleBlock (a+30,b);
}
I have a function that scans for a complete line, and runs through the linked list of blocks deleting the relevant ones and reassigning the ->next pointers.
SingleBlock *deleteBlock;
SingleBlock *tempBlock;
tempBlock = deleteBlock->next;
delete deleteBlock;
The game works, blocks are deleted correctly and everything functions as it is supposed to. However on inspection I can still access random bits of deleted data.
If I printf each of the deleted singleblocks "x" values AFTER their deletion, some of them return random garbage (confirming the deletion) and some of them return 222, telling me even though the destructor was called the data wasn't actually deleted from the heap. Many identical trials show it is always the same specific blocks that are not deleted properly.
The results:
Existing Blocks:
Block: 00E927A8
Block: 00E94290
Block: 00E942B0
Block: 00E942D0
Block: 00E942F0
Block: 00E94500
Block: 00E94520
Block: 00E94540
Block: 00E94560
Block: 00E945B0
Block: 00E945D0
Block: 00E945F0
Block: 00E94610
Block: 00E94660
Block: 00E94680
Block: 00E946A0
Deleting Blocks:
Deleting ... 00E942B0, X = 15288000
Deleting ... 00E942D0, X = 15286960
Deleting ... 00E94520, X = 15286992
Deleting ... 00E94540, X = 15270296
Deleting ... 00E94560, X = 222
Deleting ... 00E945D0, X = 15270296
Deleting ... 00E945F0, X = 222
Deleting ... 00E94610, X = 222
Deleting ... 00E94660, X = 15270296
Deleting ... 00E94680, X = 222
Is being able to access data from beyond the grave expected?
Sorry if this is a bit long winded.
Heap memory is like a bunch of blackboards. Imagine you are a teacher. While you're teaching your class, the blackboard belongs to you, and you can do whatever you want to do with it. You can scribble on it and overwrite stuff as you wish.
When the class is over and you are about to leave the room, there is no policy that requires you to erase the blackboard -- you simply hand the blackboard off to the next teacher who will generally be able to see what you wrote down.
It won't zero/change memory just yet... but at some point, the rug is going to be pulled from under your feet.
No it is certainly not predictable: it depends on how fast memory allocation/deallocation is churned.
In most cases, yes. Calling delete doesn't zero the memory.
Note that the behavior is not defined. Using certain compilers, the memory may be zeroed. When you call delete, what happens is that the memory is marked as available, so the next time someone does new, the memory may be used.
If you think about it, it's logical - when you tell the compiler that you are no longer interested in the memory (using delete), why should the computer spend time on zeroing it.
After deleting an object it's not defined what will happen to the contents of the memory that it occupied. It does mean that that memory is free to be re-used, but the implementation doesn't have to overwrite the data that was there originally and it doesn't have to reuse the memory immediately.
You shouldn't access the memory after the object is gone but it shouldn't be surpising that some data remains in tact there.
Well, I have been wondering about this for quite a while as well, and I have tried to run some tests to better understand what's going on under the hood. The standard answer is that after you call delete you should not expect anything good from accessing that memory spot. However, this did not seem enough to me. What is it really happening when calling delete(ptr)? Here's what I've found. I'm using g++ on Ubuntu 16.04, so this may play a role in the results.
What I first expected when using the delete operator was that the freed memory would be handed back to the system for usage in other processes. Let me say this does not happen under any of the circumstances I have tried.
Memory released with delete still seem to be allocated to the program it first allocated it with new. I have tried, and there is no memory usage decrease after calling delete. I had a software which allcated around 30MB of lists through new calls, and then released them with subsequent delete calls. What happened is that, looking at the System monitor while the program was running, even a long sleep after the delete calls, memory consumption my the program was the same. No decrease! This means that delete does not release memory to the system.
In fact, it looks like memory allocated by a program is his forever! However, the point is that, if deallocated, memory can be used again by the same program without having to allocate any more. I tried to allocate 15MB, freeing them, and then allocating another 15MB of data after, and the program never used 30MB. System monitor always showed it around 15MB. What I did, in respect to the previous test, was just to change the order in which things happened: half allocation, half deallocation, other half of allocation.
So, apparently memory used by a program can increase, but never shrink. I thought that maybe memory would really be released for other processes in critical situations, such as when there is no more memory available. After all, what sense would it make to let a program keep its own memory forever, when other processes are asking for it? So I allocated the 30MB again, and while deallocating them I run a
memtester
with as much physical memory I could. I expected to see my software hand out its memory to memtester. But guess it, it did not happen!I've made up a short screencast that shows the thing in action:
To be 100% honest, there was a situation in which something happened. When I tried memtester with more than the available physical memory in the middle of the deallocation process of my program, the memory used by my program dropped to around 3MB. The memtester process was killed automatically though, and what happened was even more surprising! The memory usage of my program increased with each delete call! It was just as if Ubuntu was restoring all its memory back after the memtester incident.
This is technically known as Undefined Behavior. Don't be surprised if it offers you a can of beer either.