6

With is code, I've got the following output:

A::A() is called
test #1
A::A(const A & other) is called
test #2
A::A(const A & other) is called
A::A(const A & other) is called
test #3
A::A(const A & other) is called
A::A(const A & other) is called
A::A(const A & other) is called

When debugging the code, for the 3 test cases, I found that the 1st invocation to copy constructor is the same (and I think it makes sense): make a copy of the object and push to the vector.

However, additional invocation to the copy constructor is made through "_Umove_if_noexcept".

For test #2, when vec already has ONE entry, it will further invoke ONE time of the copy constructor.

For test #3, when vec already has TWO entry, it will further invoke TWO times of the copy constructor.

This is reproducible on Visual Studio 2017 and gcc 5.4.0.

Why is this happening? Is there a performance issue?

Thanks

#include <iostream>
#include <vector>
class A
{
public:
    //constructor
    A()
    {
        a = 10;
        std::cout << "A::A() is called" << std::endl;
    }

    //copy constructor
    A(const A& other) : a(other.a)
    {
        std::cout << "A::A(const A & other) is called" << std::endl;
    }

    //assignment operator
    A& operator=(const A& other)
    {
        std::cout << "A::operator=(const A & other) is called" << std::endl;
        a = other.a;
        return *this;
    }
public:
    int a;
};

int main()
{
    std::vector<A> vec;
    //A::A() is called
    A a;

    std::cout << "test #1" << std::endl;
    //A::A(const A & other) is called by push_back
    vec.push_back(a);

    std::cout << "test #2" << std::endl;
    //A::A(const A & other) is called 
    //A::A(const A & other) is called from _Umove_if_noexcept
    vec.push_back(a);

    std::cout << "test #3" << std::endl;
    //A::A(const A & other) is called
    //A::A(const A & other) is called from _Umove_if_noexcept
    //A::A(const A & other) is called from _Umove_if_noexcept
    vec.push_back(a);   

     std::cin.get();

    return 0;
}
milesma
  • 1,561
  • 1
  • 15
  • 37

2 Answers2

13

First of all you have to remember that unless you reserve memory for the vector, it needs to allocate and reallocate memory as its capacity needs to increase.

The reallocation basically allocates new memory, then copies the element in the vector to the new memory.

Furthermore, when you push a value into a vector, that value needs to be copied into the vector. You can avoid that by emplacing the value (which means it's constructed in place in the vector), or by moving the value when you push it back.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Thanks. Just gave it a try, if I call vec.reserve(10); then test #1, #2, #3 all invoke once of the copy constructor. – milesma Aug 31 '18 at 06:41
7

The first copy is clear - the push_back() needs to copy the object.

In the second copy, two things happen:

  1. first, because the vector doesn't contain enough space, it needs to expand, and during that operation, it needs to copy all stored objects
  2. second, the object is copied
BЈовић
  • 62,405
  • 41
  • 173
  • 273