I tried to google and search an answer to my question, but I couldn't find any valid explanation hence I am posting my question here. Following is my sample code and output:
#include <iostream>
#include "vector"
using namespace std;
typedef struct Node{
int data;
Node(){
data = 0;
std::cout << "Node created. " << this <<'\n';
}
~Node(){
data = 0;
std::cout << "Node destroyed. " << this <<'\n';
}
} Node;
int main() {
std::vector<Node> vec;
for(int i = 0; i < 2 ; i++)
vec.push_back( *(new Node));
return 0;
}
Output:
Node created. 0x9e0da10
Node created. 0x9e0da30
Node destroyed. 0x9e0da20
Node destroyed. 0x9e0da40
Node destroyed. 0x9e0da44
Why is there an extra destroy and why are created objects different from destroyed object?
If you would add a copy constructor you would find out the answer:
Node created. 0x60200000df90
Node copied: 0x60200000df70 Source: 0x60200000df90
Node created. 0x60200000df50
Node copied: 0x60200000df34 Source: 0x60200000df50
Node copied: 0x60200000df30 Source: 0x60200000df70
Node destroyed. 0x60200000df70
Node destroyed. 0x60200000df34
Node destroyed. 0x60200000df30
So when you have added the second element to the vector there will not be enough capacity and the vector has to resize the storage and copy all old elements (in this case only one such element) to the new space. That's why one extra copy constructor was called.
And yes, in C++ all standard containers requires that object should be copy or move constructible, so you don't have to create them on the heap (as in Java). The more correct code would be like:
#include <iostream>
#include "vector"
struct Node {
int data;
Node() : data(0) {
std::cout << "Node created. " << this <<'\n';
}
Node(const Node &n) : data(n.data) {
std::cout << "Node copied: " << this << " Source: " << &n << std::endl;
}
~Node(){
// You don't need data = 0 in destructor
std::cout << "Node destroyed. " << this <<'\n';
}
};
int main() {
std::vector<Node> vec;
for(int i = 0; i < 2 ; i++) {
vec.push_back(Node());
}
return 0;
}
vec.push_back( *(new Node));
is an immediate memory leak.
First you dynamically allocate a Node
, then you copy that Node
into the vector. The copy operation is what creates the new object, which is why this
is different.
The original (dynamically allocated) Node
is never deallocated, but the copies are when the destructor of the vector runs (i.e. at the end of the function).
Why three destructor calls instead of two? That's caused by the automatic reallocation of the vector when you push_back
. It moves/copies its elements to a new memory location, destroying the old elements.
Note that normally, when you simply want a vector of n
default-constructed elements, you'd do:
std::vector<Node> vec(2);
This calls Node()
(the default constructor) for elements vec[0]
and vec[1]
, and you don't need a loop (or dynamic allocation).
When you add an element to a vector you construct a new copy through the copy constructor (or the move constructor).
So a line like vec.push_back( *(new Node));
does two things.
- It constructs a new Node dynamically allocated with
new
.
- It copies the new node into the vector. This means a new node is created through the copy constructor.
- The original node is never deallocated (this is called a memory leak)
This version of your code with the copy constructor might provide you with some insight : http://ideone.com/ow5YOI
Your loop is equivalent to
vec.push_back( *(new Node));
vec.push_back( *(new Node));
what happens is:
new Node
allocates memory and creates a Node (Node created)
push_back
allocates new storage
push_back
creates a Node
object in the vector's storage, using the (implicit) copy constructor. (no message printed)
- The
Node
you created is leaked (2 Nodes exist, 1 unreachable)
new Node
allocates memory and creates a Node (Node created)
push_back
allocates new storage, and copies/moves the existing elements (no message printed)
push_back
deletes its old contents (Node deleted)
push_back
creates another Node
in the vector, using the (implicit) copy constructor (no message printed)
- The
Node
you created is leaked (4 Nodes exist, 2 unreachable)
When the vector goes out of scope, the two copies it contains are deleted. (Node deleted, Node deleted)
You would normally write
vec.push_back(Node())
or
vec.emplace_back()
instead.
If you want to really see what's going on, you ought to create a non-default copy constructor:
Node(const Node& other){
data = other.data;
std::cout << "Node created. " << this
<< " from " << &other << std::endl;
}