Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion about construction/destruction in vector after std::vector::clear

Tags:

c++

#include <iostream>
#include <vector>

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

    ~A() { std::cout << "destructor" << std::endl; }
};

int main()
{
    std::vector<A> myvec;
    myvec.push_back(A());
    myvec.push_back(A());
    myvec.clear();
    return 0;
}

output:

constructor
destructor
constructor
destructor
destructor
destructor
destructor

There are five calls to the destructor here. The first two are due to the temporaries which are passed to push_back. There are three other calls, but I expected one two extra calls.

myvec.clear() will clear only two contents so destructor of A should call only two times (instead of three). why there is one extra time destructor is called?

But, if I push_back only one element to the vector, instead of two, the output is as I expected.

like image 613
MKR Harsha Avatar asked Aug 27 '15 08:08

MKR Harsha


2 Answers

The call to std::vector::clear isn't really important in this context because, when myvec goes out of scope, its contents will be destroyed anyway.


Let's consider

class A
{
public:
    A()  { std::cout << "constructor" << std::endl; }
    ~A() { std::cout << "destructor" << std::endl; }
    A (const A &) { std::cout << "A(A&)\n"; }

};

int main()
{
    std::vector<A> myvec;

    std::cout << "First\n";
    myvec.push_back(A());

    std::cout << "Second\n";
    myvec.push_back(A());

    std::cout << '\n'; // to separate the final destruction

    myvec.clear();
}

which outputs

First
constructor <-- First A _temporary_ object created when pushing_back
A(A&)       <-- vector makes a *copy* of the temporary
destructor  <-- Temporary A is destroyed
Second     
constructor <-- Second A _temporary_ object created when pushing_back
A(A&)       <-- Reallocation happens: vector can't copy because it ran out of space
A(A&)       <-- Copy of the second temporary
destructor  <-- Destroy of the temporary
destructor  <-- Destroy of the first element

destructor
destructor

If you std::vector::reserve some space, you can get rid of the copies made by the reallocation

std::vector<A> myvec;
myvec.reserve(8);

which confirms what said

First
constructor
A(A&) 
destructor
Second
constructor
A(A&) 
destructor

push_back is still making copies of the parameter: this can be further optimized by making your class moveable.

A(A&&) noexcept = default;
A& operator=(A&&) noexcept = default;

First
constructor
destructor
Second
constructor
destructor
like image 140
edmz Avatar answered Sep 23 '22 00:09

edmz


This is most likely due to the reallocation that takes place between the first and the second push_back. If you reserve some space ahead of time, then the deallocations are going to be 2 after the two push_backs, as you have expected.

like image 27
Shoe Avatar answered Sep 20 '22 00:09

Shoe