std::vector memory handling

2019-03-22 19:19发布

问题:

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?

回答1:

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;
}


回答2:

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).



回答3:

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



回答4:

Your loop is equivalent to

vec.push_back( *(new Node));
vec.push_back( *(new Node));

what happens is:

  1. new Node allocates memory and creates a Node (Node created)
  2. push_back allocates new storage
  3. push_back creates a Node object in the vector's storage, using the (implicit) copy constructor. (no message printed)
  4. The Node you created is leaked (2 Nodes exist, 1 unreachable)
  5. new Node allocates memory and creates a Node (Node created)
  6. push_back allocates new storage, and copies/moves the existing elements (no message printed)
  7. push_back deletes its old contents (Node deleted)
  8. push_back creates another Node in the vector, using the (implicit) copy constructor (no message printed)
  9. 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;
}


标签: c++ vector