Skip to main content
gamedev.net
🔒 Locked

erasing elements from vectors

Started by Crispy Jul 31, 2002 at 12:18 PM 2 replies 300+ views
Original Post
Crispy
Crispy
Hi, I wrote a piece of code that follows the following rules:
      
std::vector<HWND>Windows;

void Register(HWND pHandle) { Windows.push_back(pHandle); }

bool Unregister(HWND pHandle)
{
  for(unsigned i = 0; i < Windows.size(); i++)
    if(Windows[i] == pHandle)
       {
       Windows.erase(Windows.begin() + i);
       return true;
       }
  return false;
}

void Func()
{
  HWND SomeHandle = CreateWindowEx(...);

  Register(SomeHandle);

  //blah-blah. blaaaaaah...


  //WinAPI call - destroys the window :)

  DestroyWindow(SomeHandle);

  if(!Unregister(SomeHandle))
     cout << "failure" << endl;
  else
     cout << "success" << endl;
}
    
  
The question is: when I destroy the window by calling DestroyWindow(), SomeHandle is left untouched (well - it's still pointing to something), but Unregister(), nevertheless, fails to find a corresponding handle in the std::vector Windows. Is the entry in the vector removed or does it simply mutate into something I can't identify anymore (a memory leak, in this case)? And the same question the other way around: since...
   
//from: compiler docs

#define DECLARE_HANDLE(name) struct name##__ { int unused; }; 
typedef const struct name##__ NEAR* name
//from: windows.h

DECLARE_HANDLE(HWND)
    
I can't understand (with my knowledge) what the #define precisely does in the above code sample, or why it is written the way it is written, I presume HWND is a pointer (from NEAR*), and therefore by calling Windows.erase() on it, removes any reference to the window object itself, thus creating a hefty memory leak. Am I right? Furthermore, if the HWND is erase()'d (points to NULL?) I can't call DestroyWindow() for it anymore. The point of the question: do I need to erase() the handle in the vector after I've called DestroyWindow() (which I can't do), or is it removed automatically (can't imagine why)? Or , could someone please straighten out what I just wrote... Thanks, Crispy Edit: readability issues [edited by - crispy on July 31, 2002 1:23:24 PM]
"Literally, it means that Bob is everything you can think of, but not dead; i.e., Bob is a purple-spotted, yellow-striped bumblebee/dragon/pterodactyl hybrid with a voracious addiction to Twix candy bars, but not dead."- kSquared
Zipster
Zipster
quote:

Is the entry in the vector removed or does it simply mutate into something I can't identify anymore (a memory leak, in this case)?


The entry isn't removed, but it shouldn't be mutating into anything within the list. To ease your mind, there shouldn't be any memory leaks in this situation, as you're working with pure instances in the list and don't have to worry about pointed memory. STL handles things it allocates.

Try breakpointing the Unregister(...) function, and see what values the list is giving you, as well as the value of your handle. The DeleteWindow function might be changing it, or maybe it somehow changes somewhere else... In general, when you start working with std::list and std::vector, you use iterators to loop the list and not indices anymore. In this case it shouldn't matter, but when you move on to the list type, there aren't any indices and everything must be iterated by, er, iterators

You are probably getting tripped up in the token-pasting operators in the second block. Basically what they do is take what you pass in the macro, and paste it with the token next to the ##. Let's say you pass POO into this macro. This is the generated code:

struct POO__
{
int unused;
};

typedef const struct POO__ NEAR* POO

Or at least I hope I got that right

quote:

The point of the question: do I need to erase() the handle in the vector after I've called DestroyWindow() (which I can't do), or is it removed automatically (can't imagine why)?


If all you are doing is adding everything at the beginning, and then removing everything at the end, then erase() isn't necessary. You would want to use it if the elements in the list had some sort of importance based on their existance in the list for processing. For example, if you are keeping a list of people you like (hehe lame example ), then it's important for you to be able to remove one name at a time if you want at any time you want. If you want to just blast the whole list anyway at the end, you can just go with clear(). At any rate, they won't get removed until the variable dies, but the memory is handled by STL's allocator stuff... don't ask me about those details

[edited by - Zipster on July 31, 2002 1:53:45 PM]
Stoffel
Stoffel
Here''s how I would write your unregister function:

  
bool Unregister(HWND pHandle)
{
std::vector<HWND>::iterator wndIt =
find (Windows.begin (), Windows.end (), pHandle);
if (Windows.end () == wndIt)
return false; // window isn''t in the vector

Windows.erase (wndIt);
return true;
}


Note that erasing from the middle of a vector is an expensive (O(N)) operation. I would suggest a std::list here instead.
Crispy
Crispy
quote:
Original post by Zipster
If all you are doing is adding everything at the beginning, and then removing everything at the end, then erase() isn''t necessary


This is not the case - I''m modifying the list from time to time (but not too often). In fact - it does almost exactly what my example shows - holds references to window classes. The strange thing is - I removed my Unregistration code and only left the DestroyWindow() part intact, and guess what! even though I never delete anything from the vector, but certainly do add stuff to it, its size is constant, equal to the number of windows registered at any given time. I don''t know much about STL, but it seems to me vector is smart enough to remove entries that reference nothing from its listings (yess!!!).

quote:
Original post by Zipster
You are probably getting tripped up in the token-pasting operators in the second block. Basically what they do is take what you pass in the macro, and paste it with the token next to the ##. Let''s say you pass POO into this macro.



Nah - what puzzles me is the typedef const struct part. I mean - why so many words? Then again, M$ is all about verbosity: I mean, why on earth do you need names like MENUEX_TEMPLATE_HEADER, MENUEX_TEMPLATE_ITEM, MENUITEMTEMPLATE, MENUITEMTEMPLATEHEADER, and guess what!, they put these in the same sentence so you''d have to develop some extra literary skills (yummy!) to cruch yourself to yet another error message! It''s two AM and for me these make exactly the same sense when read backwards than when read normally.

quote:
Original post by Stoffel
Here''s how I would write your unregister function.



Thanks! I migrated.

Crispy

"Literally, it means that Bob is everything you can think of, but not dead; i.e., Bob is a purple-spotted, yellow-striped bumblebee/dragon/pterodactyl hybrid with a voracious addiction to Twix candy bars, but not dead."- kSquared

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.