Deleting Pointers from STL Vector

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • John Potter

    #16
    Re: Deleting Pointers from STL Vector

    On 2 Oct 2003 19:44:52 -0400, rmaddox@isicns. com (Randy Maddox) wrote:
    [color=blue]
    > Hanzo <hanzo@milclan. com> wrote in message news:<omhnnv4l0 nfkbdv7014dovil 94s2ang3g1@4ax. com>...[/color]
    [color=blue][color=green]
    > > if ((*i) && (*i)->CanRemove())
    > > {
    > > // grab hold before erasure
    > > CDrawableObject * toDie = *i;[/color][/color]
    [color=blue]
    > Here toDie is a COPY of the pointer referenced by the iterator i.
    > When you later set toDie to NULL (and why not just 0?) you are only
    > setting that copy, and of course not the original pointer it was
    > copied from.[/color]
    [color=blue]
    > All you need to do is to make toDie be a reference to the pointer of
    > interest, rather than a copy of it. Simply change the above
    > declaration to:[/color]
    [color=blue]
    > CDrawableObject * &toDie = *i;
    >
    > and all will work as you expect.[/color]

    I doubt it.
    [color=blue][color=green]
    > > // erase...
    > > i = g_vecGameObject s.erase(i);[/color][/color]

    Make dangling reference.
    [color=blue][color=green]
    > > // ...and kill
    > > delete toDie;[/color][/color]

    Kaboom!

    John

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.m oderated. First time posters: Do this! ]

    Comment

    • Randy Maddox

      #17
      Re: Deleting Pointers from STL Vector

      Hanzo <hanzo@milclan. com> wrote in message news:<6fopnv0ve 1deflo30v1cdofp 3im1sapidg@4ax. com>...[color=blue]
      > On 2 Oct 2003 19:44:52 -0400, rmaddox@isicns. com (Randy Maddox) wrote:
      >[color=green]
      > >Hanzo <hanzo@milclan. com> wrote in message news:<omhnnv4l0 nfkbdv7014dovil 94s2ang3g1@4ax. com>...
      > >All you need to do is to make toDie be a reference to the pointer of
      > >interest, rather than a copy of it. Simply change the above
      > >declaration to:
      > >
      > > CDrawableObject * &toDie = *i;
      > >
      > >and all will work as you expect.[/color]
      >
      > This does not work.
      >
      > 1. toDie is set to 0x00000000 after the delete and assignment to NULL.
      > m_cdPlayer1, while pointing to 0xFEEEFEEE, is still a value of
      > 0x00a72498. This evaluates to not NULL, and fails a test to see if it
      > exists.
      >
      > 2. Some other people have suggested I do this:
      >
      > if ((*i) && (*i)->CanRemove())
      > {
      > delete *i;
      > *i = NULL;
      >
      > This, also, does not work. After the delete, both *i and m_cdPlayer1
      > point to 0xFEEEFEEE (deleted), and their values are both 0x00a72498.
      > However, after *i is assigned to NULL, its value is 0x00000000, while
      > m_cdPlayer1 remains 0x00a72498.
      >
      > Ultimately, this boils down to one fact: I absolutely need a way to
      > walk a vector of base pointers, and delete/NULL them, affecting the
      > original variables that were pushed onto the vector in the first
      > place.
      >
      > Some people have also suggested smart pointers and boost, and I am
      > stuck with STLPort. Sorry.
      >
      > - Hanzo[/color]

      Sorry. Let's try again. :-)

      Given your original code sample, several people have suggested
      variations of the same solution to the apparent problem that you were
      setting the value of a local variable copy of the pointer of interest.
      I am quite surprised that you now say that none of these solutions
      appear to work, since it looks like any of them should be correct.

      Perhaps you can provide a bit more detail on how you are determining
      that the solutions offered are not working? Perhaps there is some
      other detail of your code not yet mentioned that affects what is
      happening. In any case, at least one problem has been found, although
      it appears there may also be some other issue involved as well.

      Randy.

      Comment

      • Michiel Salters

        #18
        Re: Deleting Pointers from STL Vector

        Hanzo <hanzo@milclan. com> wrote in message news:<6fopnv0ve 1deflo30v1cdofp 3im1sapidg@4ax. com>...[color=blue]
        > On 2 Oct 2003 19:44:52 -0400, rmaddox@isicns. com (Randy Maddox) wrote:
        >[color=green]
        > >Hanzo <hanzo@milclan. com> wrote in message news:<omhnnv4l0 nfkbdv7014dovil 94s2ang3g1@4ax. com>...
        > >All you need to do is to make toDie be a reference to the pointer of
        > >interest, rather than a copy of it. Simply change the above
        > >declaration to:
        > >
        > > CDrawableObject * &toDie = *i;
        > >
        > >and all will work as you expect.[/color]
        >
        > This does not work.
        >
        > 1. toDie is set to 0x00000000 after the delete and assignment to NULL.
        > m_cdPlayer1, while pointing to 0xFEEEFEEE, is still a value of
        > 0x00a72498. This evaluates to not NULL, and fails a test to see if it
        > exists.[/color]

        This is fundamentally impossible using dumb pointers. An object does not
        know what pointers point to it. m_cdPlayer1 is unknown to *m_cdPlayer1.
        No matter what you do to *m_cdPlayer1, including deletion via toDie
        will alter cdPlayer1.

        The correct solution is to use smart pointers. Smart pointers can be
        reset on deletion, especially when they are not mixed with dumb pointers.
        In particular, when toDie is an (indirect) copy of m_cdPlayer1 the
        NULLing is trivial.

        If you can't use some kind of smart pointers, you can't solve
        your problem. Dumb pointers are too dumb.

        Regards, Michiel Salters

        Comment

        • kanze@gabi-soft.fr

          #19
          Re: Deleting Pointers from STL Vector

          Hanzo <hanzo@milclan. com> wrote in message
          news:<omhnnv4l0 nfkbdv7014dovil 94s2ang3g1@4ax. com>...[color=blue]
          > I'm iterating over a vector of base class pointers and deleting those
          > which meet a certain criteria...i'm using pretty text-book code for
          > the particular delete/erasure (it's straight out of Myers' Effective
          > STL), and reads like this:[/color]
          [color=blue]
          > void CGame::RemoveDe adObjects()
          > {
          > // cleanup crew!!
          > vector<CDrawabl eObject*>::iter ator i;
          >
          > for (i = g_vecGameObject s.begin(); i != g_vecGameObject s.end();)
          > {
          > // if object is not null and can be removed...
          > if ((*i) && (*i)->CanRemove())
          > {
          > // grab hold before erasure
          > CDrawableObject * toDie = *i;[/color]
          [color=blue]
          > // erase...
          > i = g_vecGameObject s.erase(i);[/color]
          [color=blue]
          > // ...and kill
          > delete toDie;
          > toDie = NULL;
          > }
          > else
          > ++i;
          > }
          > }[/color]
          [color=blue]
          > Here's the problem: on the two lines that say delete toDie; toDie =
          > NULL; I am noticing that the original variable to which toDie is
          > pointing is not being set to NULL, just "toDie" itself.[/color]

          Normal, no. You told the compiler to set toDie to NULL. You didn't tell
          it to do anything else. In this case, setting toDie to NULL is for all
          intents and purposes a no-op, and a any halfway good optimizing compiler
          will eliminate the line completely.

          I'm not sure what you mean by "the original variable". The variable
          toDie contains a copy of the element which was in the vector. This
          element has been erased. It doesn't exist any more, so it can't be set
          to NULL.

          There may, of course, be other variables designating the element. The
          compiler has no way of knowing this, and it is your responsibility to
          manage them. There are several alternatives, according to your needs:

          - If just setting them to null is sufficient, there are different
          types of smart pointers. If you use boost::shared_p tr in the array,
          any boost::weak_ptr pointing to the object will appear to be NULL;
          in many cases, this is a sufficient solution. If for some reason,
          using boost::shared_p tr is not an alternative (although frankly, I
          cannot think of one), I have a ManagedPtr at my site which will
          automatically become null when the pointed to object is destructed;
          it is considerably less flexible than the Boost solution, however,
          since it places significant constraints on the pointed to object.

          - If you are storing the pointers in other collections, say because
          another object might want to refer to several GameObjects, then you
          also need some way of removing the pointers from the collection
          entirely, to avoid memory leaks. The simplest way to do this is
          just to use weak_ptr, as above, and scan the collection periodically
          (say each time you modify it) to suppress any weak_ptr whose objects
          no longer exist. If the collections are really large, however, this
          may have unacceptable runtime overhead. In such cases, you need
          some means of informing the collection; this implies some sort of
          notification mechanism, in which the collection is a listener. My
          ManagedPtr uses a notification to inform the pointers to be set to
          null, and could easily be adopted to handle additional
          notifications. While this is definitely overkill when the first
          solution works, if you do need notification, the added complexity
          that ManagedPtr introduces will be necessary anyway.

          - More generally, it may be that the object holding a pointer to the
          deleted object needs to do more than just delete the relationship.
          In this case, you need to use the Observer pattern. The destructor
          of the object informs all classes which have expressed an interest
          in it that it is going to die, and they do whatever they have to do.
          [color=blue]
          > In other words, if one of the CDrawableObject s* was say, a member var
          > of CGame called m_cdPlayer1, when the delete/set-to-NULL happens:[/color]
          [color=blue]
          > 1. Before the delete, toDie and m_cdPlayer1 have a value of
          > 0x00a72498.[/color]
          [color=blue]
          > 2. After the delete, both toDie and m_cdPlayer1 have their __vfptr
          > point to memory 0xFEEEFEEE (signifying a delete)[/color]

          I think you are speaking about something in the pointed to object
          itself. If both pointers point to the same address, it is normal that
          reading through either pointer will result in the same thing.
          [color=blue]
          > 3. After the set-to-null, the value of toDie becomes 0x00000000, while
          > m_cdPlayer1 remains at a value of 0x00a72498 (while still pointing to
          > 0xFEEEFEEE).[/color]

          What else should (or even could) happen. The compiler has no way of
          knowing which objects might or might not have pointers to the same place
          as toDie. Changing toDie changes toDie, and nothing else in the
          program.
          [color=blue]
          > Not the behavior I want. At this point, m_cdPlayer1 is officially
          > deleted, yet the test "if (m_cdPlayer)" fails, since it is technically
          > not NULL.[/color]
          [color=blue]
          > Any suggestions or recommendations on how to get the intended behavior
          > out of this snippet?[/color]

          See above. The easiest solution to begin with is to use
          boost::shared_p tr in the vector, and boost::weak_ptr every where else.
          But sooner or later, you'll run into a case where you need full
          notification; every professional I know of has some sort of change
          notification mechanism in his toolbox.

          --
          James Kanze GABI Software mailto:kanze@ga bi-soft.fr
          Conseils en informatique orientée objet/ http://www.gabi-soft.fr
          Beratung in objektorientier ter Datenverarbeitu ng
          11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

          [ See http://www.gotw.ca/resources/clcm.htm for info about ]
          [ comp.lang.c++.m oderated. First time posters: Do this! ]

          Comment

          • WW

            #20
            Re: Deleting Pointers from STL Vector

            Hanzo wrote:[color=blue]
            > I'm iterating over a vector of base class pointers and deleting those
            > which meet a certain criteria...i'm using pretty text-book code for
            > the particular delete/erasure (it's straight out of Myers' Effective
            > STL), and reads like this:[/color]
            [SNIP][color=blue]
            > Here's the problem: on the two lines that say delete toDie; toDie =
            > NULL; I am noticing that the original variable to which toDie is
            > pointing is not being set to NULL, just "toDie" itself.
            >
            > In other words, if one of the CDrawableObject s* was say, a member var
            > of CGame called m_cdPlayer1, when the delete/set-to-NULL happens:
            >
            > 1. Before the delete, toDie and m_cdPlayer1 have a value of
            > 0x00a72498.
            >
            > 2. After the delete, both toDie and m_cdPlayer1 have their __vfptr
            > point to memory 0xFEEEFEEE (signifying a delete)
            >
            > 3. After the set-to-null, the value of toDie becomes 0x00000000, while
            > m_cdPlayer1 remains at a value of 0x00a72498 (while still pointing to
            > 0xFEEEFEEE).
            >
            > Not the behavior I want. At this point, m_cdPlayer1 is officially
            > deleted, yet the test "if (m_cdPlayer)" fails, since it is technically
            > not NULL.
            >
            > Any suggestions or recommendations on how to get the intended behavior
            > out of this snippet?[/color]

            I see no m_Player in your code snippet. So I have no idea. However I have
            few information for you:

            1.) std::vector stores a copy of whatever thing it stores. The pointer
            inside the vector has absolutely no connection to the m_cdPlayer1 member
            variable it has been copied from.

            2.) toDie is a copy of what was inside the vector. It has no connection to
            the pointer pointed by the iterator. It makes absolutely no sense to set it
            to NULL, since in the next line (at the }) it ceases to exist.

            If you want to be able to change the original pointer, you will need to use
            a pointer to pointer and store that in the vector.

            --
            WW aka Attila



            [ See http://www.gotw.ca/resources/clcm.htm for info about ]
            [ comp.lang.c++.m oderated. First time posters: Do this! ]

            Comment

            • Alexei Polkhanov

              #21
              Re: Deleting Pointers from STL Vector

              Hanzo wrote:[color=blue]
              > I'm iterating over a vector of base class pointers and deleting those
              > which meet a certain criteria...i'm using pretty text-book code for
              > the particular delete/erasure (it's straight out of Myers' Effective
              > STL), and reads like this:
              >
              > void CGame::RemoveDe adObjects()
              > {
              > // cleanup crew!!
              > vector<CDrawabl eObject*>::iter ator i;
              >
              > for (i = g_vecGameObject s.begin(); i !=
              > g_vecGameObject s.end();)
              > {
              > // if object is not null and can be removed...
              > if ((*i) && (*i)->CanRemove())
              > {
              > // grab hold before erasure
              > CDrawableObject * toDie = *i;
              >
              > // erase...
              > i = g_vecGameObject s.erase(i);
              >
              > // ...and kill
              > delete toDie;
              > toDie = NULL;
              > }
              > else
              > ++i;
              > }
              > }[/color]
              simplier:

              for (i = g_vecGameObject s.begin(); i != g_vecGameObject s.end(); ++i)
              {
              if ((*i) && (*i)->CanRemove())
              {
              delete (*g_vecGameObje cts.erase(i));
              }
              }

              to eliminate any doubts about behavior of your code just add something like
              .....
              ~CDrawableObjec t(void) { std::cout << "~CDrawableObje ct(); " << std::endl; }
              .....

              to CDrawableObject class declaration, and observe output when destructor
              called.
              [color=blue]
              >
              > Here's the problem: on the two lines that say delete toDie; toDie =
              > NULL; I am noticing that the original variable to which toDie is
              > pointing is not being set to NULL, just "toDie" itself.
              >[/color]
              .....[color=blue]
              > out of this snippet?
              >[/color]
              what you inserting into vector in first place is a copy of pointer
              (variable m_cdPlayer) so when assign NULL (you should be using "0"
              instead, because NULL is not defined anywhere in C++ headers) you assign
              it to copy of pointer stored in a vector not to m_cdPlayer member
              variable itself.
              Maybe instead you should use map<>, eliminate m_cdPlayer member variable
              completely and address your objects like:

              .....
              iter = mElemMap["CD Player"];
              .....

              you can iterate map<> same way as you iterate vector, only difference is
              in second case, that you have only _one_ single location where you keep
              pointers to Drawable objects;


              [ See http://www.gotw.ca/resources/clcm.htm for info about ]
              [ comp.lang.c++.m oderated. First time posters: Do this! ]

              Comment

              Working...