CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 4 of 4
  1. #1
    Join Date
    Jun 2002
    Location
    Germany
    Posts
    1,557

    Question Run-time polymorphism and memory cleanup in list's

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

    You're gonna go blind staring into that box all day.

  2. #2
    Join Date
    Jun 2002
    Location
    Letchworth, UK
    Posts
    1,020
    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.
    Succinct is verbose for terse

  3. #3
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    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.
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  4. #4
    Join Date
    Jun 2002
    Location
    Germany
    Posts
    1,557

    Thumbs up

    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.

    You're gonna go blind staring into that box all day.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured