I wrote the following code:
#include <iostream>
#include <vector>
using namespace std;
class AClass
{
public:
int data;
AClass()
{ data = -333; cout << "+ Creating default " << data << endl; }
AClass(const AClass ©)
{ data = copy.data; cout << "+ Creating copy of " << data << endl; }
AClass(int d)
{ data = d; cout << "+ Creating " << data << endl; }
~AClass()
{ cout << "- Deleting " << data << endl; }
AClass& operator = (const AClass &a)
{ data = a.data; cout << "= Calling operator=" << endl; }
};
int main(void)
{
vector<AClass> v;
for (int i = 3; i--; )
v.push_back(AClass(i));
vector<AClass>::iterator it = v.begin();
while (it != v.end())
cout << it->data << endl, it++;
return 0;
}
And the output from the program is:
+ Creating 2
+ Creating copy of 2
- Deleting 2
+ Creating 1
+ Creating copy of 1
+ Creating copy of 2
- Deleting 2
- Deleting 1
+ Creating 0
+ Creating copy of 0
+ Creating copy of 2
+ Creating copy of 1
- Deleting 2
- Deleting 1
- Deleting 0
2
1
0
- Deleting 2
- Deleting 1
- Deleting 0
Then I changed the class to:
class AClass
{
public:
int data;
AClass(int d)
{ data = d; cout << "+ Creating " << data << endl; }
~AClass()
{ cout << "- Deleting " << data << endl; }
};
And the output becomes:
+ Creating 2
- Deleting 2
+ Creating 1
- Deleting 2
- Deleting 1
+ Creating 0
- Deleting 2
- Deleting 1
- Deleting 0
2
1
0
- Deleting 2
- Deleting 1
- Deleting 0
It appears that vector is making copies of existing objects when new ones are added, but it seems like a lot of unnecessary allocation/deletion is taking place. Why is this? Also, why does the second version work when I haven’t provided a copy constructor?
When you add an element, e.g. with
v.push_back(AClass(i));, what is done is a temporaryAClassobject is created and passed topush_back.push_backmust then copy this object into the container.The other reason that you see copies made is that
std::vectorstores its elements contiguously in an array. If there is no room left in the underlying array and you try to add another element to the end, thestd::vectormust create a new array, copy the elements from the old array into a new one, and then insert the new element at the end. If you don’t want this to occur, you can callstd::vector::reserveto reserve sufficient space in thestd::vectorbefore you start inserting elements, or you can use a different sequence container, likestd::deque, which does not store its elements contiguously.In a C++ program, objects are frequently created and destroyed. Note that in your program,
AClassis very cheap to copy: its size is probably four or eight bytes, just large enough to hold itsintdata member.If you have a type that is expensive to copy (e.g., perhaps you have a large tree data structure that has thousands of nodes), then yes, copying may be too expensive. In that case, you can store smart pointers to dynamically allocated objects in the
std::vectorinstead (astd::vector<shared_ptr<AClass> >, for example). If your compiler supports rvalue references and has a move-aware Standard Library implementation, you can make an expensive-to-copy type movable by implementing a move constructor and move assignment operator and usingemplace_backinstead ofpush_back.If you don’t declare a copy constructor, the compiler provides a default copy constructor for you.