🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

C++ Workshop - Pointers (Ch. 8)

Started by
87 comments, last by mihaimoldovan 12 years, 2 months ago
Yes, we do need to delete TempPointer. All memory allocated on the free store needs to be freed with delete, but memory created on the stack (declared without new) will be destroyed at the end of the current scope (the closing } brace).
{     DataType* pdt = new DataType();     DataType* dt;          //pdt will not be automatically destroyed, so we delete it before it goes out of scope     delete pdt;     //dt is destroyed here because its scope has ended}
------------------------------Support the Blue Skies in Games Campaign!A blog... of sorts.As a general rule, if you don't have a general rule in your signature, you aren't as awesome as someone who does. General rules roxor teh big one one ones.
Advertisement
Thanks that cleared up alot of things for me. I had some confusions there and learning about pointers was kind of difficult for me.
Just trying this learn this very challenging subject of game programming.
Quote: Original post by boolai
Do we still need to delete the TempPointer from memory?

It depends. In your case, NO.

Let's retrace your steps: you allocate memory dynamically within the function, then set an external pointer to reference that piece of memory. And then you delete the memory, leaving the external pointer (m_pPointer, which is the return value of the function) pointing at... what? What is the return value of your function?

(Answer: an address, but one that it is no longer legal for the program to access.)

(On a side note, your p_pointer parameter is unused. You might as well omit it from the function.)

The correct handling of memory in instances like yours, where you wish to dynamically allocate within a function and pass the allocated memory to calling code, is beyond the scope of what has been covered so far. In overview, the responsibility to clean up the memory (call delete) now rests with the calling code:
...DataType * ptr = GetPointer();// use ptrdelete ptr;

Naturally, this is error prone as it logically separates allocation from deallocation, placing them at different levels of scope. What if the calling code forgets to call delete? What if ptr get reassigned?

To combat this, there are patterns and stratagems in C++ that generally go by the name of auto pointers. What they do is wrap the raw pointer in a special type of object that then releases the memory the pointer references when it goes out of scope:

[advanced for this stage]
std::auto_ptr< DataType > ptr (GetPointer());// when ptr goes out of scope, the auto_ptr destructor will call delete on the memory internally held in a DataType pointer.

An even better stratagem is to have the allocating function wrap the memory and return an auto pointer, nullifying the possibility of a memory leak due to failure to deallocate. Note, however, that auto_ptr is a fairly basic auto pointer, and does not cleanly handle a number of cases such as copies and reassignment. The Boost Library provides a variety of policy based auto pointers that better handle such cases. Nevertheless:
std::auto_ptr< DataType > GetPointer()
{
std::auto_ptr< DataType > ptr = new DataType;

// set up

return ptr;
}

...

// in calling code:
std::auto_ptr< DataTpe > p = GetPointer();

// everything takes care of itself now
[/advanced]
Everything that you new, you must delete exactly once. Not once in the sense of lines of code that would do that deletion, but once in the sense of times that it happens when the program is run. Similarly, everything that you new[], you must delete[] exactly once. new and new[] are not the same, and you are required to pair them with the corresponding delete/delete[]. Because a pointer has no knowledge of whether it points at an element or at an array, you can't effectively write code that decides which version to use, so you should set things up such that for any pointer, it is known ahead of time whether it will point at a new thing or a new[] thing (or an existing thing). Keep in mind with pointers-to-existing thing, that if you have multiple pointers to the same thing, and you delete using one of them, the other pointers are now "dangling" - unusable until they are reset to point to something valid. If your design involves "sharing" objects, you have to be extremely careful, because there's no easy way to just "locate all pointers that point to an object and reset them".

The deletion should only happen once you are done with the pointed-at thing. In your function, you might need to delete the existing m_Pointer before doing the new, but you should not delete afterwards, because you are going to return a copy of the pointer. There's no sense in having your function return a pointer to something that you just deleted, because that pointer is already invalid.

Also, there is no need to assign to a temporary there; you can just say 'm_Pointer = new Whatever'.
Quote:
Let's retrace your steps: you allocate memory dynamically within the function, then set an external pointer to reference that piece of memory. And then you delete the memory, leaving the external pointer (m_pPointer, which is the return value of the function) pointing at... what?


I'm confused about this. Does this mean that in the following piece of code the delete on pTemp will free the memory allocated for pTemp? Eventhough pNew has a reference to the allocated memory?

DataType* pTemp = new DataType;DataType* pNew = pTemp;delete pTemp;

Quote: Original post by RinusMaximus
Quote:
Let's retrace your steps: you allocate memory dynamically within the function, then set an external pointer to reference that piece of memory. And then you delete the memory, leaving the external pointer (m_pPointer, which is the return value of the function) pointing at... what?


I'm confused about this. Does this mean that in the following piece of code the delete on pTemp will free the memory allocated for pTemp? Eventhough pNew has a reference to the allocated memory?

DataType* pTemp = new DataType;DataType* pNew = pTemp;delete pTemp;


Yes.

Pointers don't hold the memory. They are variables that points to the memory. Moreover, there is no reference counting in C++ - in your case pNew don't have a reference to the allocated memory - it just points to it, just like pTemp do.

If I reuse the web analogy (thanks Telastyn, good idea), both pointers are different URL which points to the same web site (yes, you can do this). If you remove the web site using either URL, both URL will now be invalid.

There is no magic in pointers. Pointer variables really store the memory address of a memory block (whether it has been allocated or not). No more, no less. For a pointer to be valid, it has to point to a valid memory block.

BTW, the web analogy can be taken one step further, by considering new and delete as well.
  • new -> you ask your ISP to create a web space for you. It will get a domain name from the legal authorities (allocation) and will set up the web space (call of the constructor).

  • delete -> you ask your ISP to remove a web space from the web. It will remove your site from its hard drives (call of the destructor) and will ask for the deletion of the domain name (deallocation).

HTH,
Oh, that makes sense. Now that I look back at my question I have no clue why I was confused in the first place :P
Quote: Original post by RinusMaximus
Quote:
Let's retrace your steps: you allocate memory dynamically within the function, then set an external pointer to reference that piece of memory. And then you delete the memory, leaving the external pointer (m_pPointer, which is the return value of the function) pointing at... what?


I'm confused about this. Does this mean that in the following piece of code the delete on pTemp will free the memory allocated for pTemp? Eventhough pNew has a reference to the allocated memory?

DataType* pTemp = new DataType;DataType* pNew = pTemp;delete pTemp;


Yes. 'delete' says "see this chunk of memory? I don't need it any more." The system does not try to account for other variables that might also point to it, because (a) it can't do so safely (it is possible, although a bad idea, to store pointer values into non-pointer types); (b) if it could keep track of such things ([google] "garbage collection"), you wouldn't need 'delete' at all. Remember, C++ is a low-level language. The burden for this sort of thing generally falls on the program.
That also means that pNew won't leak because it was not allocated? But it was in the stack so the function will delete it for us? But we can no longer use pNew because it is pointing to an address that is no longer valid?
Just trying this learn this very challenging subject of game programming.
Quote: Original post by boolai
That also means that pNew won't leak because it was not allocated?


Both pNew and pTemp pointed to a single piece of allocated memory. All c++ cares about is that someone will pass the pointer it returns with new back to delete. But yes, you are right. Since pNew never was set to a piece of allocated memory that did not "belong" to someone else, it wont leak as someone else ( pTemp ) is responsible for deleteing the memory.

How do I know it is responsible? I just made it up right now. So we could change the code so that pTemp has a comment indicating that the memory it points to is shared, but that pTemp will be the official "owner", and so is responsible for deletion. A more idiomatic way of doing this would be to make pTemp an std::auto_ptr, like Oluseyi explained.

Quote:
But it was in the stack so the function will delete it for us?


Now, when you start talking about the stack you are confusing yourself. pNew itself needs some space on the stack, it needs ( for 32 machines ) 4 bytes of space to store the address that it points at. These 4 bytes will be freed at the end of the function, but the memory pointed at by pNew will not be touched.

What we pointed it at, the memory that new returned, was not on the stack, so will not be automatically freed.


Quote:
But we can no longer use pNew because it is pointing to an address that is no longer valid?


Yes, after the line "delete pTemp;", all pointers that are pointing at the same place are invalid and should not be dereferenced.

They can, and should, be set to 0 or NULL to indicate that they are no longer valid.

This topic is closed to new replies.

Advertisement