#include <iostream>
#include <vector>
struct T{
T(){
std::cout << "Constructor\n";
}
~T(){
std::cout << "Destructor\n";
}
};
int main() {
std::vector<T> vec;
vec.push_back(T());
vec.push_back(T());
return 0;
}
The output is:
(1)Constructor
(2)Destructor
(3)Constructor
(4)Destructor
(5)Destructor
(6)Destructor
(7)Destructor
Why there is so much desructors calls? I see that:
(1) consruct temporary object temp1
(2) destruct temp1
(3) consruct temporary object temp2
(4) destruct temp2
Then it was called copy constructor or move constructor for temp1 and temp 2. So, (5) and (6) are clear. But what about (7)?
Let's expand your structure a bit:
struct T {
T() {
std::cout << "Constructor\n";
}
T(const T&) {
std::cout << "Copy Constructor\n";
}
T(T&&) {
std::cout << "Move Constructor\n";
}
~T() {
std::cout << "Destructor\n";
}
};
And separate calls to push_back
method:
vec.push_back(T()); // 1
std::cout << "--- --- ---\n";
vec.push_back(T()); // 2
std::cout << "--- --- ---\n";
Now the output looks more complete:
Constructor
Move Constructor
Destructor
--- --- ---
Constructor
Move Constructor
Copy Constructor
Destructor
Destructor
--- --- ---
Destructor
Destructor
The first group:
Constructor
Move Constructor
Destructor
corresponds to the first push_back
call:
vec.push_back(T()); // 1
The output might be decrypted easily:
Constructor // Create a temporary
Move Constructor // Move a temporary into the internal vector storage
Destructor // Destroy a temporary
The second group:
Constructor
Move Constructor
Copy Constructor
Destructor
Destructor
corresponds to the second push_back
call:
vec.push_back(T()); // 2
and a little bit more complicated:
Constructor // create a temporary
Move Constructor // move it into the newly allocated vector storage
Copy Constructor // copy previously created element into the new storage
Destructor // destroy old storage
Destructor // destroy temporary
Here you should remember that vector class allocates its memory internally and then manages it to provide enogh space for all elements. So, if you add more elements, new allocations happen and old elements are copied or moved into the new storage.
In case of known size you might use reserve
method, which simply reserves enough memory for a particular number of elements. It allows to avoid unnecessary memory reallocations and copying or moving elements during these reallocations on adding new elements into the vector (at least until you don't exceed the reserved size).
The third group:
Destructor
Destructor
corresponds to the vector vec
destructor call at the end of the program.
emplace_back matters for "move only" types (like std::unique_ptr).
This is wrong and an oversimplification. Not all containers are created equally. For your vector example, if we use reserve
the implementation can do a move assign rather than a construct, eliding our copy/extraneous destructor:
std::vector<T> v;
v.reserve(2);
v.emplace_back(1);
v.emplace_back(1);
Output:
Constructor
Constructor
---
Destructor
Destructor
For a set:
std::set<T> s;
s.emplace(1);
s.emplace(1);
We get the same output. Why though? A set should theoretically construct only one object since sets are unique right? In actuality, the typical implementation constructs a temporary node in order to perform the comparison, accounting for the extra construct/destruct even though it never enters the container.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.