std::list of class pointers, understanding problem (with minimal example)

Discussion in 'C++' started by Frank Steinmetzger, Jul 28, 2010.

  1. Hello group

    I am trying to solve a segfault in a project of mine. To better understand
    what’s going on, I wrote a minimal program to see how deletion of list
    elements behaves if the list stores only pointers to class instances.

    Looking at the STL source confirmed what I suspected - the instance is not
    deleted when erasing the list item and its pointer, so I did not find the
    solution to my primary problem yet.

    But I found something else in the program that I am curious about. It:
    - defines a class k with a private member and an accessor get()
    - uses std::list<k*> to store some instances of k
    - has an output function that iterates through a list and outputs the
    value of the class pointer and the result of pointer->get()
    - declares two lists and fills them with three identical class instances
    - deletes the middle item in list 1 and destroys its instance of k

    List 2 has now, to my understanding, an invalid pointer in item 2, so it
    should segfault when calling the output function, should it not?

    But instead, the output function prints out "0". Where am I wrong here?
    Thanks in advance for your time.


    Here’s the program (EDIT: I know it doesn’t clean up at the end, but I
    omitted it for a smaller posting :))


    #include <iostream>
    #include <list>

    using namespace std;

    class k {
    private:
    int i;
    public:
    k(int _i) {i=_i;};
    int get() {return i;}
    ~k() {cout << "destroyed #" << i << endl;}
    };


    void output(list<k*> &l) {
    for (list<k*>::iterator i=l.begin(); i!=l.end(); i++)
    cout << (*i) << ": " << (*i)->get() << endl;
    cout << endl;
    }

    int main (int argc, char* argv[]) {
    list<k*> list1,list2;
    k* pk;
    for (int i=1; i<4; i++) {
    pk=new k(i);
    list1.push_back(pk);
    list2.push_back(pk);
    }
    output(list1); output(list2);

    list<k*>::iterator it=list1.begin();
    it++;
    delete(*it);
    list1.erase(it);

    output(list1); output(list2);
    return 0;
    }

    --
    Gruß | Greetings | Qapla'
    Mein Gewissen ist rein! denn ich habe es nie benutzt!
     
    Frank Steinmetzger, Jul 28, 2010
    #1
    1. Advertising

  2. Re: std::list of class pointers, understanding problem (withminimal example)

    * Frank Steinmetzger:
    > Hello group
    >
    > I am trying to solve a segfault in a project of mine. To better understand
    > what???s going on, I wrote a minimal program to see how deletion of list
    > elements behaves if the list stores only pointers to class instances.
    >
    > Looking at the STL source confirmed what I suspected - the instance is not
    > deleted when erasing the list item and its pointer, so I did not find the
    > solution to my primary problem yet.


    Try a list of boost::shared_ptr<type *>

    > But I found something else in the program that I am curious about. It:
    > - defines a class k with a private member and an accessor get()
    > - uses std::list<k*> to store some instances of k
    > - has an output function that iterates through a list and outputs the
    > value of the class pointer and the result of pointer->get()
    > - declares two lists and fills them with three identical class instances
    > - deletes the middle item in list 1 and destroys its instance of k
    >
    > List 2 has now, to my understanding, an invalid pointer in item 2, so it
    > should segfault when calling the output function, should it not?


    Not necessarily. Yes, the item in list 2 will be dangling, and accessing
    it will trigger undefined behaviour.

    "undefined behaviour" is, well, undefined. It may include generating a
    segfault, it may include giving wrong answers, it might just set fire
    to your harddisk. There's no way of telling, it's *undefined*.

    In your case, I'd even go as far as saying that it is *unlikely* that
    a segmentation fault will occur.

    --
    Martijn van Buul -
     
    Martijn van Buul, Jul 28, 2010
    #2
    1. Advertising

  3. Frank Steinmetzger <> wrote:
    > List 2 has now, to my understanding, an invalid pointer in item 2, so it
    > should segfault when calling the output function, should it not?


    No. Dereferencing an invalid pointer is undefined behavior. "Undefined
    behavior" in no way implies "segfault". It could segfault, or it could do
    something completely different (for example, seemingly working just fine).

    (I don't know about dereferencing a null pointer, though. I think that
    at least some operating systems guarantee a segfault in that case, but
    probably not the C++ standard.)
     
    Juha Nieminen, Jul 28, 2010
    #3
  4. Frank Steinmetzger

    Goran Pusic Guest

    Re: std::list of class pointers, understanding problem (with minimalexample)

    On Jul 28, 1:00 pm, Frank Steinmetzger <> wrote:
    > Hello group
    >
    > I am trying to solve a segfault in a project of mine. To better understand
    > what’s going on, I wrote a minimal program to see how deletion of list
    > elements behaves if the list stores only pointers to class instances.
    >
    > Looking at the STL source confirmed what I suspected - the instance is not
    > deleted when erasing the list item and its pointer, so I did not find the
    > solution to my primary problem yet.
    >
    > But I found something else in the program that I am curious about. It:
    > - defines a class k with a private member and an accessor get()
    > - uses std::list<k*> to store some instances of k
    > - has an output function that iterates through a list and outputs the
    >   value of the class pointer and the result of pointer->get()
    > - declares two lists and fills them with three identical class instances
    > - deletes the middle item in list 1 and destroys its instance of k
    >
    > List 2 has now, to my understanding, an invalid pointer in item 2, so it
    > should segfault when calling the output function, should it not?
    >
    > But instead, the output function prints out "0". Where am I wrong here?
    > Thanks in advance for your time.
    >
    > Here’s the program (EDIT: I know it doesn’t clean up at the end, but I
    > omitted it for a smaller posting :))
    >
    > #include <iostream>
    > #include <list>
    >
    > using namespace std;
    >
    > class k {
    > private:
    >     int i;
    > public:
    >     k(int _i) {i=_i;};
    >     int get() {return i;}
    >     ~k() {cout << "destroyed #" << i << endl;}
    >
    > };
    >
    > void output(list<k*> &l) {
    >     for (list<k*>::iterator i=l.begin(); i!=l.end(); i++)
    >         cout << (*i) << ": " << (*i)->get() << endl;
    >     cout << endl;
    >
    > }
    >
    > int main (int argc, char* argv[]) {
    >     list<k*> list1,list2;
    >     k* pk;
    >     for (int i=1; i<4; i++) {
    >         pk=new k(i);
    >         list1.push_back(pk);
    >         list2.push_back(pk);
    >     }
    >     output(list1); output(list2);
    >
    >     list<k*>::iterator it=list1.begin();
    >     it++;
    >     delete(*it);
    >     list1.erase(it);
    >
    >     output(list1); output(list2);
    >     return 0;
    >
    > }


    You store pointers to same objects in two lists. Then you remove one
    pointer from one list and you delete object this pointer points to.
    The other list still contains the same pointer, but that pointer
    points to... well, undefined behavior, as said. This is the source of
    your problem.

    You must understand this first: in C and C++ (and some other
    languages, but the root cause of your trouble here is C), you manage
    heap manually. You must know where your pointers point to and where
    they are, at any point in time. You must, if you delete object pointed
    to some pointer, never again try to access said object (there is no
    object anymore). That means, if you have several pointers to one
    object, and you delete said object, you must do something with all
    these pointers (e.g. track them down and set them all to NULL).

    In your example, you could do:

    list<k*>::iterator it=list1.begin();
    it++;
    delete(*it);
    list1.erase(it);
    // Remove^^^ pointer from list 2
    it=list2.begin();
    it++;
    list2.erase(it);

    ^^^ but NOT delete, as deleting twice is undefined behavior.

    Alternatively, you could use list<shared_ptr<k> > (NOT
    list<shared_ptr<k*> > as suggested by Martijn). This is because
    shared_ptr (of boost, or upcoming C++0x library) handles object
    lifetime for you using a technique called "reference counting".

    Goran.
     
    Goran Pusic, Jul 29, 2010
    #4
  5. Goran Pusic wrote:

    > You store pointers to same objects in two lists. Then you remove one
    > pointer from one list and you delete object this pointer points to.
    > The other list still contains the same pointer, but that pointer
    > points to... well, undefined behavior, as said. This is the source of
    > your problem.


    I know what I wrote in that program, it was deliberate, because I thought
    that program would crash, but it didn’t. The two lists were just a means of
    keeping the dangling pointer around. I was wondering why I could access a
    method of a class instance that was no longer there. My interpretation would
    be that, similar to deleting a file on disk where only the inode was deleted
    and not the file itself, the bytes in memory were still there and not
    overwritten with other stuff, so the binary code could still be executed
    without a segfault.

    > You must understand this first: in C and C++ (and some other
    > languages, but the root cause of your trouble here is C), you manage
    > heap manually. You must know where your pointers point to and where
    > they are, at any point in time. You must, if you delete object pointed
    > to some pointer, never again try to access said object (there is no
    > object anymore).


    I know my way around pointers alright, I’ve been using them when I was but
    an early teenager, writing Pascal programs on a DOS machine. The main topic
    for my posting here was not why I get the segfault in this other project I
    mentioned, but why the ouput function returned a 0 instead of crashing the
    program due to a memory access violation, as I would have expected.
    --
    Gruß | Greetings | Qapla'
    Rien ne va minus!
     
    Frank Steinmetzger, Jul 29, 2010
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. jimjim
    Replies:
    18
    Views:
    7,406
    Default User
    Apr 12, 2004
  2. Siegfried Heintze
    Replies:
    0
    Views:
    470
    Siegfried Heintze
    Nov 25, 2006
  3. Replies:
    6
    Views:
    665
    Jim Langston
    Oct 30, 2005
  4. Frank Steinmetzger
    Replies:
    0
    Views:
    286
    Frank Steinmetzger
    Jul 28, 2010
  5. Replies:
    9
    Views:
    294
Loading...

Share This Page