getsuha
getsuha

Reputation: 711

Interesting extra destruction call during push_back in std::vector

I find the output of the following code very interesting.

class Name
{
  string _name;
public:
  Name(const string& name) : _name(name) { cout << "ctor of " << _name << endl; }
  ~Name(){ cout << "dtor of " << _name << endl; }
};
int main() {

  vector<Name> list;
  cout << "------------------START push_back" << endl;
  list.push_back(Name(string("A")));
  cout << "------------------push_back(A) performed..." << endl;
  list.push_back(Name(string("B")));
  cout << "------------------push_back(B) performed..." << endl;
  list.push_back(Name(string("C")));
  cout << "------------------push_back(C) performed..." << endl;
  cout << "------------------END push_back" << endl;

  return 0;
}

I can understand that push_back uses an extra temporary object which is why emplace_back is recommended for better performance. Can someone explain the extra destructor calls shown in the output below with (???) next to it.

------------------START push_back
ctor of A
dtor of A
------------------push_back(A) performed...
ctor of B
dtor of A(???)
dtor of B
------------------push_back(B) performed...
ctor of C
dtor of A(???)
dtor of B(???)
dtor of C
------------------push_back(C) performed...
------------------END push_back
dtor of A
dtor of B
dtor of C
------------------START emplace_back
ctor of A
------------------emplace_back(A) performed...
ctor of B
------------------emplace_back(B) performed...
ctor of C
------------------emplace_back(C) performed...
------------------END emplace_back()
dtor of A
dtor of B
dtor of C

Upvotes: 1

Views: 62

Answers (2)

SherAndrei
SherAndrei

Reputation: 627

std::vector is a dynamic array.

Every time there isn't enough memory allocated to store elements, std::vector is trying to reallocate for a greater chunk of memory to store more elements. After that it needs to copy (move) your elements to other chunk implicitly calling dtors for previous elements.

To understand what is happening try to extend your class:

class Name
{
  string _name;
public:
  Name(const string& name) : _name(name) { cout << "ctor of " << _name << endl; }
  Name(const Name& other) : _name(other._name) { cout << "copy ctor for " << _name << endl; }

  ~Name(){ cout << "dtor of " << _name << endl; }
};

Rerun your program now and see how many copy ctors were called.

Now try to call

vector<Name> list;
list.reserve(3);
/* push_backs */

and enjoy :)

You can find more about vectors here and about vector member function reserve here

Upvotes: 1

eerorika
eerorika

Reputation: 238401

Can someone explain the extra destructor calls shown in the output below with (???) next to it.

The size of a memory allocation cannot change. The size of an array correspondingly cannot change since the memory allocated for the array cannot change.

The elements of a vector are stored in a single block of allocated memory, forming an array. If the memory block cannot change size, then how is it possible to add elements to a vector? Well, what we can do is allocate a larger block of memory, then copy (move) the elements from the smaller block to the larger one, then destroy the old elements in the old block, and finally deallocate the old block. This is what a vector does when you add more elements than fit within the block of memory that it has allocated for its elements (which is called capacity).

Can someone explain the extra destructor calls shown in the output below with (???) next to it.

Those are the destroyed elements from the old memory when the vector reallocates.

In case you're wondering why there is no output from the construction of the objects in the new memory, that is because the move constructor of your class produces no output.

In case you're wondering why there is no extra destruction after every added element, that is because vector doesn't just allocate memory for a single element when growing. Instead, it multiplies the capacity by some factor (typically implementation use a factor of 2 or 1.5). This results in better asymptotic complexity for inserting high number of elements when the final number of elements isn't initially known.

Upvotes: 1

Related Questions