Reputation: 115
When I run this code destructor starts before object removal.
Code is here:
#include <string>
#include <vector>
using namespace std;
class Testi {
public:
string name;
Testi(string a) : name(a) {
cout << "Im alive: " << name << endl;
}
~Testi() {
cout << "Im no longer alive: " << name << endl;
}
};
int main() {
vector <Testi> a;
a.push_back(Testi("John"));
a.push_back(Testi("Jack"));
a.push_back(Testi("Jake"));
cout << a[1].name;
cin.get();
return 0;
}
When i run program output is:
Im alive: John
Im no longer alive: John
Im alive: Jack
Im no longer alive: John
Im no longer alive: Jack
Im alive: Jake
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: JakeJack
And after input:
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake
So after every push_back() all destructors runs. The output operation works good so objects still exist. For the 1st one destructor runs 4 times! Why?
Upvotes: 1
Views: 126
Reputation: 5665
If you would overload the copy and move constructors (and track instances for convenience) you will see what is going on there
struct Testi {
static int instanceCount;
std::string name;
int instanceIndex;
Testi(Testi&& other) : name(other.name), instanceIndex(++instanceCount) {
std::cout <<other.instanceIndex << " => " << instanceIndex << " move constructor " << name << std::endl;
}
Testi(const Testi& other) : name(other.name), instanceIndex(++instanceCount) {
std::cout <<other.instanceIndex << " => " << instanceIndex << " copy constructor " << name << std::endl;
}
Testi(std::string a) : name(a), instanceIndex(++instanceCount) {
std::cout << instanceIndex << " Im alive: " << name << std::endl;
}
~Testi() {
std::cout << instanceIndex << " Im no longer alive: " << name << std::endl;
}
};
int Testi::instanceCount = 0;
1 Im alive: John
1 => 2 move constructor John
1 Im no longer alive: John
3 Im alive: Jack
3 => 4 move constructor Jack
2 => 5 copy constructor John
2 Im no longer alive: John
3 Im no longer alive: Jack
6 Im alive: Jake
6 => 7 move constructor Jake
5 => 8 copy constructor John
4 => 9 copy constructor Jack
5 Im no longer alive: John
4 Im no longer alive: Jack
6 Im no longer alive: Jake
Jack8 Im no longer alive: John
9 Im no longer alive: Jack
7 Im no longer alive: Jake
1 - temporary object that is being passed as an argument to push_back()
2 - object stored in the vector created by moving (or copying)
3 - another temporary object
4 - object that is stored in a vector
5 - copy of the object 2 because vector needs to reallocate
And so on. But this will give you general idea of implementation (and why one might want to reserve()
). Standard does not restrict what exactly should be used. Here we should also talks about versions:
push_back()
contained type should be CopyInsertable and MoveInsertableThis should give you general expectations of implementation.
Upvotes: 0
Reputation: 310930
To make it more clear add the copy constructor for the class for example the following way as it is shown in this demonstrative program
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Testi {
public:
string name;
Testi(const string &a) : name(a) {
cout << "Im alive: " << name << endl;
}
Testi( const Testi &t ) : name(t.name + "_copy") {
cout << "Im alive: " << name << endl;
}
~Testi() {
cout << "Im no longer alive: " << name << endl;
}
};
int main()
{
{
vector <Testi> a;
a.push_back(Testi("John"));
a.push_back(Testi("Jack"));
a.push_back(Testi("Jake"));
cout << "---------------------" << endl;
for (const auto &item : a) cout << item.name << ' ';
cout << endl << endl;
}
{
cout << "---------------------" << endl;
vector <Testi> a;
a.reserve(3);
a.emplace_back("John");
a.emplace_back("Jack");
a.emplace_back("Jake");
cout << "---------------------" << endl;
for (const auto &item : a) cout << item.name << ' ';
cout << endl << endl;
}
return 0;
}
Its output might look like
Im alive: John
Im alive: John_copy
Im no longer alive: John
Im alive: Jack
Im alive: John_copy_copy
Im no longer alive: John_copy
Im alive: Jack_copy
Im no longer alive: Jack
Im alive: Jake
Im alive: John_copy_copy_copy
Im alive: Jack_copy_copy
Im no longer alive: John_copy_copy
Im no longer alive: Jack_copy
Im alive: Jake_copy
Im no longer alive: Jake
---------------------
John_copy_copy_copy Jack_copy_copy Jake_copy
Im no longer alive: John_copy_copy_copy
Im no longer alive: Jack_copy_copy
Im no longer alive: Jake_copy
---------------------
Im alive: John
Im alive: Jack
Im alive: Jake
---------------------
John Jack Jake
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake
So in this statement
a.push_back(Testi("John"));
there is created a temporary object as result of the expression Testi("John")
.
Then this object is copied to the vector and the vector stores a copy of the temporary object. At the end of the statement the temporary object is deleted.
Im alive: John
Im alive: John_copy
Im no longer alive: John
When this statement is executed
a.push_back(Testi("Jack"));
the same operations are performed except that the vector needs to reallocate memory that to accommodate the new element.
Im alive: Jack
Im alive: John_copy_copy
Im no longer alive: John_copy
Im alive: Jack_copy
Im no longer alive: Jack
the first message corresponds to creating the temporary object that corresponds to the argument Testi("Jack")
. Then the current element of the vector is copied to the new extent of memory due to the memory reallocation
Im alive: John_copy_copy
Im no longer alive: John_copy
then the new element is copied and the temporary object is deleted
Im alive: Jack_copy
Im no longer alive: Jack
and so on.
If you will reserve enough memory in the vector then there will no memory reallocation. Also if you will use emplace_back
instead of push_back
there will not be created temporary objects. In this case the output will be
Im alive: John
Im alive: Jack
Im alive: Jake
and
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake
Upvotes: 2
Reputation: 47954
Here's the relevant piece of code:
vector <Testi> a;
a.push_back(Testi("John"));
Testi("John")
creates a new, temporary Testi object.push_back
copies that object into the vector.So the unexpected constructor and destructor calls come from the creation and deletion of the temporary. You can avoid the extra temporaries and copies by using emplace_back
which will construct the object directly in the vector.
Upvotes: 4
Reputation: 18338
You're passing a temporary object as a parameter to push_back
. the temporary is being copied and after that its lifetime expires, thus it's destroyed.
Upvotes: 0