In order to understand what freeing memory means, you must first understand what allocating memory means. What follows is a simplified explanation.
There exists memory. Memory is a large blob of stuff that you could access. But since it's global, you need some way to portion it out. Some way to govern who can access which pieces of memory. One of the systems that governs the apportionment of memory is called the "heap".
The heap owns some quantity of memory (some is owned by the stack and some is owned by static data, but nevermind that now). At the beginning of your program, the heap says that you have access to no heap-owned memory.
What new int
does is two fold. First, it goes to the heap system and says, "I want a piece of memory suitable to store an int
into." It gets back a pointer to exactly that: a piece of the heap, into which you can safely store and retrieve exactly one value of type int
.
You are now the proud owner of one int
's worth of memory. The heap guarantees that as long as its rules are followed, whatever you put there will be preserved until you explicitly change it. This is the covenant between you and the almighty heap.
The other thing new int
does is initialize that piece of the heap with an int
value. In this case, it is default initialized, because no value was passed (new int(5)
would initialize it with the value 5).
From this point forward, you are legally allowed to store exactly one int
in this piece of memory. You are allowed to retrieve the int
stored there. And you're allowed to do one other thing: tell the heap that you are finished using that memory.
When you call delete p
, two things happen. First, p
is deinitialized. Again, because it is an int
, nothing happens. If this were a class, its destructor would be called.
But after that, delete
goes out to the heap and says, "Hey heap: remember this pointer to an int
you gave me? I'm done with it now." The heap system can do whatever it wants. Maybe it will clear the memory, as some heaps do in debug-builds. In release builds however, the memory may not be cleared.
Of course, the reason why the heap can do whatever it wants is because, the moment you delete that pointer, you enter into a new agreement with the heap. Previously, you asked for a piece of memory for an int
, and the heap obliged. You owned that memory, and the heap guaranteed that it was yours for as long as you wanted. Stuff you put there would remain there.
After you had your fun, you returned it to the heap. And here's where the contract comes in. When you say delete p
, for any object p
, you are saying the following:
I solemnly swear not to touch this memory address again!
Now, the heap might give that memory address back to you if you call new int
again. It might give you a different one. But you only have access to memory allocated by the heap during the time between new
and delete
.
Given this, what does this mean?
delete p;
cout << *p << "\t" << p << "\n";
In C++ parlance, this is called "undefined behavior". The C++ specification has a lot of things that are stated to be "undefined". When you trigger undefined behavior anything can happen! *p
could be 0. *p
could be the value it used to be. Doing *p
could crash your program.
The C++ specification is a contract between you and your compiler/computer. It says what you can do, and it says how the system responds. "Undefined behavior" is what happens when you break the contract, when you do something the C++ specification says you aren't supposed to. At that point, anything can happen.
When you called delete p
, you told the system that you were finished using p
. By using it again, you were lying to the system. And therefore, the system no longer has to abide by any rules, like storing the values you want to store. Or continuing to run. Or not spawning demons from your nose. Or whatever.
You broke the rules. And you must suffer the consequences.
So no, delete p
is not the equivalent of *p = 0
. The latter simply means "set 0 into the memory pointed to by p
." The former means "I'm finished using the memory pointed to by p
, and I won't use it again until you tell me I can."
*p = 0
even work for e.g.std::string
? – Poppy