milesleft
milesleft

Reputation: 43

C++: Adding automatically allocated objects to a std::vector

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 &copy) 
        { 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?

Upvotes: 4

Views: 2642

Answers (4)

James McNellis
James McNellis

Reputation: 355069

It appears that vector is making copies of existing objects when new ones are added

When you add an element, e.g. with v.push_back(AClass(i));, what is done is a temporary AClass object is created and passed to push_back. push_back must then copy this object into the container.

The other reason that you see copies made is that std::vector stores 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, the std::vector must 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 call std::vector::reserve to reserve sufficient space in the std::vector before you start inserting elements, or you can use a different sequence container, like std::deque, which does not store its elements contiguously.

it seems like a lot of unnecessary allocation/deletion is taking place

In a C++ program, objects are frequently created and destroyed. Note that in your program, AClass is very cheap to copy: its size is probably four or eight bytes, just large enough to hold its int data 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::vector instead (a std::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 using emplace_back instead of push_back.

why does the second version work when I haven't provided a copy constructor?

If you don't declare a copy constructor, the compiler provides a default copy constructor for you.

Upvotes: 5

Lou Franco
Lou Franco

Reputation: 89172

Vector uses a regular array of T as its store -- when you create one of these, it has to initialize the space somehow, and the only option is a default constructor. Later, when you set the value of an index, it copies it onto that space.

In the second version, even if you don't provide a copy constructor, one is generated for you automatically. If you declare a private one, and then don't implement it, you will see an compiler error (because you have suppressed the generation of the default)

Upvotes: 5

Ali1S232
Ali1S232

Reputation: 3411

first of all copy cunstructor isusualy generated by c++ itself if you don't provide one, and vector is copying all the data since it's not sure if the variable you gave him has valid value whenever you asked for it, for example you can use some local variable and pass it to the vector, if vector doesn't copy what you gave it, and you return that vector, there will be some memory violations. whever you add some new object to it and it needs a bigger array to store all the object it allocate a new array, and then copy all the existing data into the new array.

Upvotes: 0

Cat Plus Plus
Cat Plus Plus

Reputation: 129764

Your objects are copied, because vector is expanding its internal storage. Call vector::reserve beforehand to preallocate memory, if you want to avoid the copies. If you don't provide your own copy ctor, compiler will generate one for you (one that copies all members).

Upvotes: 3

Related Questions