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

F

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.

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;
}
 
M

Martijn van Buul

* 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 said:
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.
 
J

Juha Nieminen

Frank Steinmetzger said:
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.)
 
G

Goran Pusic

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.
 
F

Frank Steinmetzger

Goran said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top