Click to See Complete Forum and Search --> : Run-time polymorphism and memory cleanup in list's


dude_1967
July 8th, 2002, 06:48 AM
Gurus,

I am uncertain of the proper use of run-time polymorphism and memory cleanup in STL-list.

Often one manages lists of pointers to objects in an STL-list. Imagine then a list of pointers to base, whereby base has two derived classes derived1 and derived2.

The approach shown in the code snippet below is my standard usage, and it seems to work well with VC and GNU. Is the use of run-time polymorphism correct in the code example below? Is it guaranteed in C++ that (*it)->f() must properly interpret the type of the pointed to object?

In addition, look at the memory cleanup. The right destructors will be called, OK. Is it also necessary to remove the objects from the list itself using "erase"?.

Thanks. Chris.

#include <list>
#include <iostream>
using namespace std;

class base
{
private:
int m1;
public:
base() { m1 = 1; }
virtual ~base() { cout << "delete c1" << endl; }
virtual void f(void) { cout << m1 << " " << "function1" << endl; }
};

class derived1 : public base
{
private:
int m2;
public:
derived1() { m2 = 2; }
virtual ~derived1() { cout << "delete c2" << endl; }
virtual void f(void) { cout << m2 << " " << "function2" << endl; }
};

class derived2 : public derived1
{
private:
int m3;
public:
derived2() { m3 = 3; }
virtual ~derived2() { cout << "delete c3" << endl; }
virtual void f(void) { cout << m3 << " " << "function3" << endl; }
};

int main(int argc, char* argv[])
{
list<base*> ptr_list;
base* p;

// Add a new base to list
p = new base;
ptr_list.push_back(p);

// Add a new derived1 to list
p = new derived1;
ptr_list.push_back(p);

// Add a new derived2 to list
p = new derived2;
ptr_list.push_back(p);

list<base*>::iterator it;

// Do some kind of operation on the list elements
for(it = ptr_list.begin(); it != ptr_list.end(); it++)
{
(*it)->f();
}

// Memory cleanup
for(it = ptr_list.begin(); it != ptr_list.end(); it++)
{
// Is this correct?
// Is it enough memory cleanup?
// Is it necessary to remove elements from the list?
delete *it;
}

return 1;
}

:)

cup
July 8th, 2002, 07:00 AM
Yes, you will need an erase to remove the objects in the list after the for loop.

If you check the length of ptr_list at the end of the loop, it will tell you that the list still contains 3 objects, even though trying to access *(ptr_list.begin ()) will probably crash your system. That space needs to be freed up.

Graham
July 8th, 2002, 07:45 AM
First point: (*it)->f() should work fine, given that f() is virtual in base.

In the specific example given, cleanup of the list is not necessary, because it immediately goes out of scope after the deletes. In general, however, you may want to do a clear() on the list to avoid re-using the pointers.

It's important to note, however, that relying on being able to delete all the objects is not exception-safe. For that, you need a list of smart pointers (not auto_ptr!) which will do the delete in their destructors (boost::shared_pointer is a good one to start with). That way, the delete will happen even if an exception is thrown. A good rule of thumb is that if you have a naked pointer anywhere, then your code is probably not exception safe and you should consider wrapping the pointer.

Also, just as a couple of side issues, try to get into the habit of using pre-increment, rather than post-increment unless you really need the previous value of the incremented object. For some classes, using post-increment unnecessarily can be very wasteful. Another habit to get into is to use initialiser lists in ctors, rather than assignment in the ctor body. Putting the assignment in the body means that you do a default initialisation and and an assignment which could be wasteful on some classes. Plus, there are some things (e.g. references) that have to be intialised in an intialiser list.

dude_1967
July 8th, 2002, 07:57 AM
Thanks for the information / suggestions.

Thanks cup. Some type of clear is needed since size() == 3.

Thanks Graham. All of your suggestions are good. This is not the first time that others have suggested that I use smart pointers.

Chris.

:)