2

I'm trying to understand vectors of classes. First the class:

class A {
public:
    A() {   cout << "A ctor" << endl; }
    ~A() { cout << "A dtor" << endl; }
};

Then the code:

int main() {
    vector<A> v;
    cout << "after vector created" << endl;
    v.push_back(A());
    cout << "after one A added" << endl;
}

If I run the program, the output is:

after vector created
A ctor
A dtor   <<-- why this??
after one A added
A dtor

I do not understand, what is the additional destructor call. Shouldn't the constructor and destructor calls be paired?

Secondly, if I pass the vector by value to a function, why are the constructors are not called?

void f(vector<A> v);

I checked, that f has copy of A, since if I modify it's internal state, it's not modified in the original vector and &v[0] returns different value inside f.

Niall
  • 30,036
  • 10
  • 99
  • 142
Oliv
  • 10,221
  • 3
  • 55
  • 76
  • 2
    that's because vector::push_back always makes a copy of the parameter you just pass in, but via copy-constructor. the werid destructor is invoked by the temporaray A object, that is, the one you passed in. Also, if you want to create an element within vector directly, you can use emplace_back, which is supported by C++11. – Kingsley Chen Jul 17 '15 at 10:39
  • as you were told above, is the object you pass in. A destructor is called when the object goes out of scope. The A() you passed in is local to the push_back function. Once it goes out of scope its destructor is called. –  Jul 17 '15 at 10:53
  • I had a related question that might be of interest: http://stackoverflow.com/questions/28716209/what-operators-do-i-have-to-overload-to-see-all-operations-when-passing-an-objec – 463035818_is_not_an_ai Jul 17 '15 at 11:11
  • Thanks for all the answers, I selected the one mentioning C++11 stuff. – Oliv Jul 17 '15 at 11:26

6 Answers6

3

You are not getting the full picture because you are not tracing the copy constructor:

class A {
public:
    A() {   cout << "A ctor" << endl; }
    ~A() { cout << "A dtor" << endl; }
    //trace copy constructor
    A(const A&) { cout << "A copy ctor" << endl; }
};

Now the output is:

after vector created
A ctor
A copy ctor
A dtor
after one A added
A dtor

You create a temporary object of type A to pass in to push_back. This is then copied using the copy constructor, the temporary is destroyed, then the copy is destroyed along with the std::vector at the end of the main function.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • While I am afraid of editing answers of 15k-rating people I still did it with adding `emplace_back`. So, any reason to remove it? Just curious. – VP. Jul 17 '15 at 10:53
  • @VictorPolevoy That's a good suggestion, but you should add it in an answer of your own: I'm just directly answering the question. – TartanLlama Jul 17 '15 at 10:55
  • Well, I've thought it is good to elaborate good answers to make them more better. – VP. Jul 17 '15 at 10:55
  • @VictorPolevoy: True in general, but there is a limit. You added too much and effectively changed what this answer is saying. "Elaborating" other peoples' answers is frowned on for the most part. – Lightness Races in Orbit Jul 17 '15 at 11:03
  • Sorry then, that was I afraid of. Did not mean to make the answer worse. – VP. Jul 17 '15 at 11:05
  • @TartanLlama what do you mean by a `meta post`? Can you please explain? – VP. Jul 17 '15 at 11:06
  • 1
    @TartanLlama [Question on meta](http://meta.stackoverflow.com/questions/299523/when-should-i-elaborate-answers-to-make-them-better). – VP. Jul 17 '15 at 11:21
3

Shouldn't the constructor and destructor calls be paired?

Yes, they are "paired"; for every object created, its destructor will be called. Essentially what you are doing is because you are not tracing out the implicitly generated copy constructor here. It is generated by the temporary A() and since the class A has no move associated with it, a copy of this temporary is inserted into the vector. If the class A had a move constructor, it would be used to "move" the object into the vector and hence you would see the move constructor being used instead of the copy constructor.

Once C++11 and up is included in the mix, there are a few members that may be involved here; copy constructors, move constructors and assignment operators are included.

#include <iostream>
#include <vector>

using namespace std;

class A {
public:
    A() { cout << "A ctor" << endl; }
    ~A() { cout << "A dtor" << endl; }
    A(const A&) { cout << "A copy ctor" << endl; }
    A(A&&) { cout << "A move ctor" << endl; }
    A& operator=(const A&) { cout << "An assignment" << endl; return *this; }
    A& operator=(A&&) { cout << "A move assignment" << endl; return *this; }
};

int main() {
    vector<A> v;
    cout << "after vector created" << endl;
    v.push_back(A());
    cout << "after one A added" << endl;
}

With the output;

after vector created
A ctor
A move ctor
A dtor
after one A added
A dtor

With respect to the second query;

...if I pass the vector by value to a function, why are the constructors are not called?

They are, the copy constructors would be used to copy the contents of the vector. If using C++11, you could also move the entire vector when call the function (if the vector would no longer be required in the calling function).

Niall
  • 30,036
  • 10
  • 99
  • 142
2

If you see the definition of push_back of the vector:

// [23.2.4.3] modifiers
 /**
  *  @brief  Add data to the end of the %vector.
  *  @param  __x  Data to be added.
  *
  *  This is a typical stack operation.  The function creates an
  *  element at the end of the %vector and assigns the given data
  *  to it.  Due to the nature of a %vector this operation can be
  *  done in constant time if the %vector has preallocated space
  *  available.
  */
 void
 push_back(const value_type& __x)
 {
   if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
     {
       _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                                __x);
       ++this->_M_impl._M_finish;
     }
   else
#if __cplusplus >= 201103L
     _M_emplace_back_aux(__x);
#else
     _M_insert_aux(end(), __x);
#endif
 }

#if __cplusplus >= 201103L
    void
    push_back(value_type&& __x)
    { 
        emplace_back(std::move(__x)); 
    }
#endif

Here it is easy to see what is push_back doing:

  1. In older standards (less than C++11) it just passes an object by copy thus vector creates a copy of the object you passed to the push_back.
  2. In newer standards (>= C++11) it simply uses move-constructor which avoids the copying but still creates an object by using move-constructor.

To avoid this you may use the emplace_back:

class A {
public:
    A(int i = 0) {   cout << "A ctor" << endl; }
    ~A() { cout << "A dtor" << endl; }
};

int main() {
    vector<A> v;
    cout << "after vector created" << endl;
    v.emplace_back();
    cout << "after one A added" << endl;
}

This will just create an object of class A inside vector's memory space by passing an arguments of the method to the constructor of the class object, so it would not be copied or moved:

after vector created
A ctor
after one A added
A dtor

Test emplace_back

P.S. About what you got:

after vector created
A ctor
A dtor
after one A added
A dtor

You don't see ctor-dtor paired here because in std::vector::push_back as I mentioned above does not construct an object of class A through the default constructor (in which you print A ctor) but a copy-constructor or a move-constructor (depends on a standard with what you compile and implementation details).

So, it is actually paired but you just don't see it because you did not implement (override implicitly-declared) move and copy constructors. If you'll do this, you'll see something like this:

A default constructor
A copy/move constructor
A destructor
A destructor
VP.
  • 15,509
  • 17
  • 91
  • 161
1

Yes, they're paired. For every dtor call, there is one ctor call. That may be a default ctor (which prints "A ctor") or a copy ctor (which prints nothing - it's compiler generated).

MSalters
  • 173,980
  • 10
  • 155
  • 350
1

In the line v.push_back(A()); the argument of type A you put in the method is a result of the constructor call A(), I think you can easily understand why the constructor is called. Now keep in mind that the passed argument will live until the push_back method return. So at the end of the push_back method, your object will be destroyed. Here's a simple example

if( ... )
{
     int a = 42;
}
a = 12; //error: use of undeclared identifier 'a'

a live in the scope of the if statement, so when you live the statement a is destroyed. Same thing for your argument.

Secondly why your constructor is not called here void f(vector<A> v); ? It's because a copy constructor is called. You pass your already constructed object to the method by value, so a copy is made. But you did not define a copy constructor by yourself, so the compiler created one for you and call it.

If you want to see how it works check this Live example i made.

If you don't know what a copy constructor is check this

Pumkko
  • 1,523
  • 15
  • 19
0

Because the A object is copied into the vector, and therefore the temporary object that you pass in the call to push_back() is destroyed right after the copy.

This is what happens:

vector<A> v;
cout << "after vector created" << endl;
v.push_back(A()); 
// <<-- A temporary A object is created by calling A(). Then, the copy constructor of A is called to insert it into the vector.
// The temporary A object is destroyed.
cout << "after one A added" << endl;
// The A object in the vector is destroyed
Samuel Navarro Lou
  • 1,168
  • 6
  • 17