views:

931

answers:

5

I have a Foo object, and a std::list holding instances of it. My problem is that when I add a new instance to the list, it first calls the ctor but then also the dtor. And then the dtor on another instance (according to the this pointer).

A single instance is added to the list but since its dtor (along with its parents) is called, the object cant be used as expected.

Heres some simplified code to illustrate the problem:

#include <iostream>
#include <list>

class Foo
{
public:
    Foo()
    { 
     int breakpoint = 0;
    }
    ~Foo()
    { 
     int breakpoint = 0;
    }
};

int main()
{
    std::list<Foo> li;
    li.push_back(Foo());
}
+10  A: 

When you push_back() your Foo object, the object is copied to the list's internal data structures, therefore the Dtor and the Ctor of another instance are called.

All standard STL container types in C++ take their items by value, therefore copying them as needed. For example, whenever a vector needs to grow, it is possible that all values in the vector get copied.

Maybe you want to store pointers instead of objects in the list. By doing that, only the pointers get copied instead of the object. But, by doing so, you have to make sure to delete the objects once you are done:

for (std::list<Foo*>::iterator it = list.begin(); it != list.end(); ++it) {
    delete *it;
}
list.clear();

Alternatively, you can try to use some kind of 'smart pointer' class, for example from the Boost libraries.

Ferdinand Beyer
Ah! That explains alot, so thats why the instances doesnt work since they dont have a proper copy constructor defined? Looks like pointers is my best bet then.
mizipzor
mizipzor: You should decide when writing a class if it should have a copy ctor and operator=. If not you should declare private prototypes for those. You will then get an error if your code tries to use the copy ctor or operator=.
Binary Worrier
The Boost Pointer Container library (http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html) is worth mentioning here. For example, a boost::ptr_vector<Foo> is more efficient and provides a nicer interface than the "equivalent" std::vector<boost::shared_ptr<Foo> >
Éric Malenfant
A: 

Making the list holding pointers instead of instances solves the problem with the destructor being called. But I still want to understand why it happens.

#include <iostream>
#include <list>

class Foo
{
public:
    Foo()
    { 
     int breakpoint = 0;
    }
    ~Foo()
    { 
     int breakpoint = 0;
    }
};

int main()
{
    std::list<Foo*> li;
    li.push_back(new Foo());
}
mizipzor
+4  A: 

You are creating a temporary Foo here:

li.push_back( Foo() )

push_back copies that Foo into its internal data structures. The temporary Foo is destroyed after push_back has been executed, which will call the destructor.

You will need a proper copy constructor that increases some reference count on the class members that you do not want to destroy early -- or make it private to force yourself on the pointer solution.

Timbo
+1  A: 

What actually happens here is that you store a copy of the passed object in the list, because you're sending it by value instead of by reference. So the first dtor that is called is actually called on the object you pass to the push_back method, but a new instance had been created by then and it is now stored in the list.

If you don't want a copy of the Foo object to be created, store pointers to Foo objects in the list instead of the objects themselves. Of course when doing it you will have to properly release memory on destruction of the list.

Stas
+2  A: 

Use this object to understand:

class Foo
{
public:
    Foo(int x): m_x(x)
    { 
    std::cout << "Constructed Object: " << m_x << ")\n";
    }
    Foo(Foo const& c): m_x(c.m_x+100)
    {
    std::cout << "Copied Object: " << m_x << ")\n";
    }
    ~Foo()
    {  
    std::cout << "Destroyed Object: " << m_x << ")\n";
    }
};

The First main

std::list<Foo*> li;
li.push_back(Foo(1));

Here we create a temporary Foo object and call push_back(). The temporary object gets copied into the list and the function returns. On completion of this statement the temporary object is then destroyed (via the destructor). When the list is destroyed it will also destroy all the obejcts it contains (Foo is an object with a destructor so destruction includes calling the destructor).

So you should see somthing like this:

Constructed Object: 1
Constructed Object: 101
DestroyedObject: 1
DestroyedObject: 101

In the second example you have:

std::list<Foo*> li;
li.push_back(new Foo(1));

Here you dynamically create an object on the heap. Then call the push_back(). Here the pointer is copied into the list (the pointer has no constructor/destructor) so nothing else happens. The list now contains a pointer to the object on the heap. When the function returns nothing else is done. When the list is destroyed it destroys (note the subtle difference betweens destroy and delete) the object it contains (a pointer) but a pointer has no destructor so nothing happens any you will leak memory.

So you should see somthing like this:

Constructed Object: 1
Martin York