problems of storing dynamically created objects in a vector

J

Jess

Hello,

I have a program that stores dynamically created objects into a
vector.

#include<iostream>
#include<vector>

using namespace std;

class A{
public:
A(){cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
};

int main(){
vector<A> v;
v.push_back(*new A);
v.push_back(*new A);
v.push_back(*new A);

return 0;
}

I output the messages for A() and ~A() to see what's been constructed/
destructed. Here is the output,

A()
A()
~A()
A()
~A()
~A()
~A()
~A()
~A()

This is quite surprising, as I expected the output to be

A()
A()
A()
~A()
~A()
~A()

because the program makes three new A objects and pushed them onto the
vector, hence I think there should be three consecutive A(), without
a "~A()" between the 2nd and 3rd "A()". When the vector is destroyed,
I think all its stored objects are destroyed, hence there should be
three "~A()". What's my mistake?

I also tried to store the pointers into the vector:

#include<iostream>
#include<vector>

using namespace std;

class A{
public:
A(){cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
};

int main(){
vector<A*> v;
v.push_back(new A);
v.push_back(new A);
v.push_back(new A);

return 0;
}

Now the output is only

A()
A()
A()

It seems the three objects are never destroyed. I guess when a vector
is destroyed, if its contents are pointers, then the pointers are
destroyed, but not the pointed objects. Is this correct?

Finally, can someone please tell me what's the correct way to store
dynamically created objects into a vector?

Thanks,
Jess
 
C

Charles Bailey

#include<iostream>
#include<vector>

using namespace std;

class A{
public:
A(){cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
};

Before going any further, add a logging copy constructor (and a
logging assignment operator for completeness). You are missing a lot
of detail because you are logging all destructions, but not all
constructions. Something like:

A(const A&)
{ cout << "A(const A&)" << endl; }
A& operator=(const A&)
{ cout << "op=(const A&)" << endl; return *this; }
int main(){
vector<A> v;
v.push_back(*new A);
v.push_back(*new A);
v.push_back(*new A);

return 0;
}

Here you are creating three new A objects and pushing a *copy* of each
into the vector. You are then never destroying the original objects.
int main(){
vector<A*> v;
v.push_back(new A);
v.push_back(new A);
v.push_back(new A);

return 0;
}

It seems the three objects are never destroyed. I guess when a vector
is destroyed, if its contents are pointers, then the pointers are
destroyed, but not the pointed objects. Is this correct?

This is correct by the compiler. If you follow this pattern and you
have no other copy of the pointers through which to delete the objects
then you must be sure to manually delete each pointer in the vector
before either removing that pointer from the vector or letting the
vector by destroyed (e.g. by going out of scope).
 
I

Ian Collins

Jess said:
Hello,

I have a program that stores dynamically created objects into a
vector.

#include<iostream>
#include<vector>

using namespace std;

class A{
public:
A(){cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
};

int main(){
vector<A> v;
v.push_back(*new A);
v.push_back(*new A);
v.push_back(*new A);

return 0;
}

I output the messages for A() and ~A() to see what's been constructed/
destructed. Here is the output,

A()
A()
~A()
A()
~A()
~A()
~A()
~A()
~A()

This is quite surprising, as I expected the output to be
You are passing objects by value to push_back, when you do this the
compiler is using the compiler generated copy constructor to create a
temporary object and then destroying it. That's why you see more
destructor the constructor calls. Try adding your own copy constructor
and see this for your self:

A(const A&){cout << "A(const A&)" << endl;}
A()
A()
A()
~A()
~A()
~A()
Which it could be if the compiler were to optimise away the copies.
Now the output is only

A()
A()
A()

It seems the three objects are never destroyed. I guess when a vector
is destroyed, if its contents are pointers, then the pointers are
destroyed, but not the pointed objects. Is this correct?
It is.
Finally, can someone please tell me what's the correct way to store
dynamically created objects into a vector?
It depends on the size of the object, for small objects you may as well
sore by value, for larger ones, store a pointer and clean up when you
erase elements from the vector. For this, I would suggest you use some
form of smart pointer to do the hose keeping for you.
 
B

BobR

Jess said:
Hello,
I have a program that stores dynamically created objects into a
vector.
[snip]

Read the other posts first, if you haven't.

Putting it all together (assuming you don't want to use a 'smart pointer',
as Ian mentioned), here's a little demo:

// #includes here
#include<iostream>
#include<vector>
#include <sstream>
std::eek:stringstream out;

class A7{ public:
A7(){ out<<"A7()"<<std::endl;}
~A7(){ out<<"~A7()"<<std::endl;}
A7( A7 const &){ out<<"A7(const A7&)"<<std::endl;}
// A7& operator=( A7 const &){
// out<<"operator=( A7 const &)"<<std::endl;
// return *this;
// }
};

int main(){ using std::cout; // for NG post
cout<<"\n - A7 test -"<<std::endl;
{
std::vector<A7> svA7( 3 );
out<<"- destructing -"<<std::endl;
}
out<<"-----"<<std::endl;
{
std::vector<A7*> svA7( 3 );
// -- fill it --
for( std::vector<A7*>::iterator i( svA7.begin() );
i != svA7.end(); ++i ){
*i = new A7;
}
out<<"-----"<<std::endl;
// -- clean it out --
for( std::vector<A7*>::iterator i( svA7.begin() );
i != svA7.end(); ++i ){
delete *i;
// *i = 0; // in case it gets called again (if function)
}
}
cout<<out.str()<<std::endl;
out.clear(); out.str("");
cout<<"\n - A7 test - END"<<std::endl;
return 0;
}// main()

[ Why 'out'? Because that's the way I needed to try it in my TestBench pgm,
and I just didn't feel like changing them all back to std::cout. <G> ]
 
B

BobR

Ian Collins wrote in message...
ref:
It depends on the size of the object, for small objects you may as well
sore by value, for larger ones, store a pointer and clean up when you
erase elements from the vector. For this, I would suggest you use some
form of smart pointer to do the hose keeping for you.

Hi Ian,

Sometimes 'typos' are bad, sometimes they are good.
By the time I got through 'sore by value' and 'hose keeping', I was in
belly-laughs. Thanks, I needed that. ;-}
 
I

Ian Collins

BobR said:
Ian Collins wrote in message...

Hi Ian,

Sometimes 'typos' are bad, sometimes they are good.
By the time I got through 'sore by value' and 'hose keeping', I was in
belly-laughs. Thanks, I needed that. ;-}
That'll teach me to type and talk on the phone at the same time!
 
J

Jess

Jess said:
Hello,
I have a program that stores dynamically created objects into a
vector.
[snip]

Read the other posts first, if you haven't.

Putting it all together (assuming you don't want to use a 'smart pointer',
as Ian mentioned), here's a little demo:

// #includes here
#include<iostream>
#include<vector>
#include <sstream>
std::eek:stringstream out;

class A7{ public:
A7(){ out<<"A7()"<<std::endl;}
~A7(){ out<<"~A7()"<<std::endl;}
A7( A7 const &){ out<<"A7(const A7&)"<<std::endl;}
// A7& operator=( A7 const &){
// out<<"operator=( A7 const &)"<<std::endl;
// return *this;
// }
};

int main(){ using std::cout; // for NG post
cout<<"\n - A7 test -"<<std::endl;
{
std::vector<A7> svA7( 3 );
out<<"- destructing -"<<std::endl;
}
out<<"-----"<<std::endl;
{
std::vector<A7*> svA7( 3 );
// -- fill it --
for( std::vector<A7*>::iterator i( svA7.begin() );
i != svA7.end(); ++i ){
*i = new A7;
}
out<<"-----"<<std::endl;
// -- clean it out --
for( std::vector<A7*>::iterator i( svA7.begin() );
i != svA7.end(); ++i ){
delete *i;
// *i = 0; // in case it gets called again (if function)
}
}
cout<<out.str()<<std::endl;
out.clear(); out.str("");
cout<<"\n - A7 test - END"<<std::endl;
return 0;
}// main()

[ Why 'out'? Because that's the way I needed to try it in my TestBench pgm,
and I just didn't feel like changing them all back to std::cout. <G> ]


Thanks! I've run the example on my computer and here's what I've got:

- A7 test -
A7()
A7(const A7&)
A7(const A7&)
A7(const A7&)
~A7()
- destructing -
~A7()
~A7()
~A7()
-----
A7()
A7()
A7()
-----
~A7()
~A7()
~A7()

Where does the first A() come from? I guess when the vector of three
A7 objects is constructed perhaps one A7 object is created first and
then copy constructor is called three times to copy the initial object
to the vector. Then the first ~A() means the initial A7 object is
destroyed? When is it destroyed? Furthermore, does a vector always
create an initial object and then destroy with the vector finishes
with it? I think this is what Ian meant? Is there any way that the
compiler doesn't create a temporary object?

Thanks,
Jess
 
C

Charles Bailey

- A7 test -
A7()
A7(const A7&)
A7(const A7&)
A7(const A7&)
~A7()
- destructing -
~A7()
~A7()
~A7()
-----
A7()
A7()
A7()
-----
~A7()
~A7()
~A7()

Where does the first A() come from? I guess when the vector of three
A7 objects is constructed perhaps one A7 object is created first and
then copy constructor is called three times to copy the initial object
to the vector. Then the first ~A() means the initial A7 object is
destroyed? When is it destroyed? Furthermore, does a vector always
create an initial object and then destroy with the vector finishes
with it? I think this is what Ian meant? Is there any way that the
compiler doesn't create a temporary object?

Thanks,
Jess

When the initial vector of size three is created the vector
implementation needs to create three instances of A which are
equivalent to default constructed As. Some classes can be optimized
to share common data so sometimes it's more efficient to create a set
of objects by copying a template object several times rather that
constructing them identically but independently. This is what this
vector implementation has chosen to do. It creates a blank template
object, copies it three times into the new vector and throws the
template away. As far as I'm aware it would have been perfectly valid
for the vector to have default constructed the three objects in which
case your logging would have shown as below, which might have been
what you expected initially.
- A7 test -
A7()
A7()
A7()
- destructing -
~A7()
~A7()
~A7()
-----

Part of the contract that your objects must comply to if you want to
place them in a vector is that copying them must "work". You
shouldn't rely on whether or how the implementation uses temporaries
or how often and when it will copy instances of your class, but
equally you should be aware of how vector will interact with your
class when you design it.
 
B

BobR

Jess wrote in message...

// > > A7(){ out<<"A7()"<<std::endl;}
A7() : MyVal(5){ out<<"A7()"<<std::endl;}

MyVal = aa.MyVal;
// Thing = new MyThing; // not Thing = aa.Thing!!!
// in copy-Ctor too? Try it. don't forget to delete Thing.

int MyVal;
// MyThing *Thing; // = new MyThing;
Thanks! I've run the example on my computer and here's what I've got:
[snip same output I got on GCC(MinGW)]
Where does the first A() come from? I guess when the vector of three
A7 objects is constructed perhaps one A7 object is created first and
then copy constructor is called three times to copy the initial object
to the vector. Then the first ~A() means the initial A7 object is
destroyed? When is it destroyed?

( see Mr. Bailey's post )
I put the 'destructing' line in so you could see that the temporary (if one)
does not get destructed by going out of scope. The vectors constructor
handles it.

Now experiment; add a couple 'push_back' ops to see what happens:
out<<"- push_back -"<<std::endl;
{ // an extra scope ....
A7 tmp;
svA7.push_back( tmp );
svA7.push_back( tmp );
out<<"- push_back done -"<<std::endl;
} // ....to cause 'tmp' to destruct
// you mignt think that because 'tmp' no longer exists, the
// 'svA7' objects were now invalid. Are they (hint: cout)?
// ( put an 'int MyVal;' in your class, and output that.)
Furthermore, does a vector always
create an initial object and then destroy with the vector finishes
with it?

The temporary 'A7' you see? It's not guaranteed (AFAIK), left to the
implementation.
When the vector goes out-of-scope, or is otherwise destructed, it calls the
destructors for all the objects it holds. If the vector holds pointers, it
destructs the pointers, but not what they point to ( that's your
responsibility, or the 'smart-pointers' if you use them (that's their strong
point).).

You'll notice in your class that I showed (but commented-out) the
'operator=()'. That *may* become important if you put something in your
class, like a pointer to an dynamically created ('new') object. If you
simply copy the pointer content to a next A7 object, you now have two
pointers to the same memory area. One A7 gets destroyed, which deletes the
dyn. object, and then leaves the copy holding a pointer to something that no
longer exists - UB! So, add something to your class, uncomment the
assignment operator, and do some experiments (vecA = vecB;, etc..). Do not
I think this is what Ian meant? Is there any way that the
compiler doesn't create a temporary object?

Did you see temps in the output of the second section (where 'new' was
used)?
(ok, bad example ('cause it's pointers). Implementation thing again.)

[ corrections always welcome ]
 
B

BobR

BobR wrote in message...
So, add something to your class, uncomment the
assignment operator, and do some experiments
** (vecA = vecB;, etc..). **

......more like:
std::vector<A7> svA7_2;
svA7_2 = svA7; // just copies vector
svA7_2.at(0) = svA7.at(1); // for operator=()
.... or just
svA7.at(0) = svA7.at(1); // for operator=()


[ corrections always welcome ]
 
J

James Kanze

[Code severely cut...]
Thanks! I've run the example on my computer and here's what I've got:
- A7 test -
A7()
A7(const A7&)
A7(const A7&)
A7(const A7&)
~A7()
- destructing -
Where does the first A() come from?

What's the second argument to the constructor of the vector in
svA7?

The STL has been designed to work with classes without default
constructors, so it never uses a default constructor internally,
only the copy constructor or assignment.
I guess when the vector of three A7 objects is constructed
perhaps one A7 object is created first and then copy
constructor is called three times to copy the initial object
to the vector.

It's required, because the constructor is actually:

vector<T>::vector( size_type n, T const& = T() ) ;

Try replacing the default constructor with one which requires an
argument (say an int). You can still write:

vector< A7 > svA7( 3, A7( 0 ) ) ;

for example.
Then the first ~A() means the initial A7 object is
destroyed?

The first ~A() is the destructor of the temporary object which
was passed as the second argument to the constructor of vector.
When is it destroyed?

As soon as we return from the constructor of vector.
Furthermore, does a vector always create an initial object and
then destroy with the vector finishes with it?

The vector doesn't. You must always supply it with an object
(or objects) to be copied, if the vector is not empty.
I think this is what Ian meant? Is there any way that the
compiler doesn't create a temporary object?

The standard requires it. I don't think that there's anyway you
(or the compiler) can avoid it.
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top