tags:

views:

346

answers:

5

Let's say I have a generic Object class, and a generic List class. I want to maintain a list of these Objects. Should I store them as List<Object> or List<Object*>?

If I use List<Object> and I have a method like:

if(some_condition) {
    Object obj;
    myObjectList.append(obj);
}

And my list class only keeps a reference to the object, so as soon as that if statement terminates, the object is destroyed, and the object I pushed becomes invalid. So then I end up doing something like:

Object *obj = new Object;
myObjectList.append(*obj);

So that it doesn't get destroyed. But now the objects are undeletable, no? Because now they're stored safely in the List as Objects, not pointers to Objects, so I can't call delete on them... or will they automatically be destroyed when they're popped from the list?

In that case, I should probably use List<Object*> and delete them from the list when I'm done with them, no?

So confused... I'm sure I have a fundamental misunderstanding here somewhere.

+2  A: 

EDIT: Sorry, I forgot that the constructor to shared pointers are explicit (meaning that you need to do a little more work:

object_list.push_back(QSharedPointer<Object>(new Object));

or

object_list.push_back(boost::shared_ptr<Object>(new Object));

Also, as mentioned in a comment boost::ptr_list is even better since it is more efficient and has the same net effect as a std::list<boost::shared_ptr<T> >.


EDIT: You mention that you are using Qt in your comment. If you are using >= 4.5 you can use Qt's QList and QSharedPointer classes like this:

QList<QSharedPointer<Object> > object_list;
object_list.push_back(new Object);


I would recommend you use std::list<>. You also likely just want to store pointers to the objects so they aren't being copied all of the time.

So bottom line is:

Let's say you have a class named Object. You should do this:

std::list<boost::shared_ptr<Object> > object_list;
object_list.push_back(new Object);

By using shared pointers, the objects will get cleaned up automatically when they are removed from the list (if there are no other shared_ptrS also pointing to it).

You could have a list<Object *>. But given your experience level, I feel that a reference counted pointer would be much easier for you to work with.

In that case, I should probably use List and delete them from the list when I'm done with them, no?

Yes, this is a viable option, but I highly recommend smart pointers to avoid the "...and delete them from the list..." step entirely.


EDIT:

also the example code you gave:

Object *obj = new Object;
myObjectList.append(*obj);

is probably not what you wanted, this makes a new object on the heap, they puts a copy of that in the list. If there is no delete obj after that, then you have a memory leak since raw pointers are not automatically deleted.

Evan Teran
Well, I'd prefer to use QList as I'm working with Qt. But I think your suggestion still works.
Mark
QList<> will work nearly identically to std::list<>. They are both templated and both can contain pointers/smart pointers.
Evan Teran
QList<> also supports STL style iterators so they are nice and compatible with the generic c++ algorithms.
Evan Teran
Supposing I didn't use shard_ptrs... I would have to delete the objects as I popped them off, right?
Mark
yes, that's exactly what you'd need to do.
Evan Teran
Oh...Qt has a shared pointer class too? Then I don't have to include boost at all... that's awesome. Thanks. You win :D
Mark
Actually... Now I'm getting "no matching function for call to ‘QList<QSharedPointer<Object> >::push_back(Object*)’" but that's exactly what you told me to do...
Mark
If you're bringing in Boost (as in `boost::shared_ptr`), then you should probably mention `boost::ptr_list`, which does the same thing better. However, `list<shared_ptr>` trick as described is still useful if one sticks to TR1 only.
Pavel Minaev
@Mark: sorry about that, I forgot about the explicit constructors or shared pointers. I've made an edit to include how to use them **properly** :-).
Evan Teran
lol. now your answer is just a mess, but it should work now all the same. thanks :)
Mark
+3  A: 

In the first case the Object is copied and only the original is destroyed. The other instance is kept in the list.

In the second version you can use the List destructor to delete the stored objects.

stribika
Oh... I see what was happening. Because with the code I'm working on, I have another pointer thrown into the mix too... I think one was invalid, and one wasn't. That explains a lot.
Mark
Make sure you have a 'deep' copy constructor, or you might get some really un-expected results.
DeusAduro
+1  A: 

Use pointers, as was suggested. You are using "generic" objects, probably a base class, and list will actually contain some derived objects casted to base -- this means you must pass pointer to originally created object. Otherwise you will cull all the polymorfism out.

That is

class Base 
{ 
public:
    int x; 
    virtual int getData() const { return x; }
};
class Derived : public Base 
{ 
public:
    int y; 
    virtual int getData() const { return y; }
};

Derived obj1;
obj1.x = 1;
obj1.y = 2;

Base obj2 = obj1;
cout << obj2.getData();

This will print 1, since obj2 is just a copy of Base portion of obj1 and is really an instance of Base.

Eugene
+1  A: 

You could use List, and then when it's time to deallocate an object at position x, you would use something like

void erase_object(list* l, int i) {
    delete (*list)[x]
    list -> removeObj((*list)[x]);
}
computergeek6
+1  A: 

as with "all" things there is no one answer - the devil's in the details of what you want to do, or what your constraints are.

If the objects are lightweight and do not involve deep copying it's going to be more efficient to store the little things as copies. Otherwise, the overhead of smartish pointers is more than is warranted. If you are polymorPHic then you could use templated lists.

if it's more important to minimize copying, and eliminate redundant code from template instantiations, then use a list of smart, shared pointers.

Or use a list of naked pointers, use new/delete, and be meticulous with your pointer ownership.

Any list will act like a list, so the choice of list implementation is dependent on factors you aren't enumerating here.