Подтвердить что ты не робот

Std::vector обработка памяти

Я попытался найти Google и найти ответ на свой вопрос, но я не смог найти никакого действительного объяснения, поэтому я размещаю свой вопрос здесь. Ниже приведен пример кода и вывода:

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

Вывод:

Node created. 0x9e0da10
Node created. 0x9e0da30
Node destroyed. 0x9e0da20
Node destroyed. 0x9e0da40
Node destroyed. 0x9e0da44

Почему существует дополнительный разрушение и почему создаются объекты, отличные от уничтоженного объекта?

4b9b3361

Ответ 1

Если вы добавите конструктор копирования, вы узнаете ответ:

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

Итак, когда вы добавили второй элемент в вектор, не будет достаточной емкости, и вектор должен будет изменить размер хранилища и скопировать все старые элементы (в данном случае только один такой элемент) в новое пространство. Вот почему был вызван один дополнительный конструктор копии.

И да, на С++ все стандартные контейнеры требуют, чтобы объект был скопирован или перемещен конструктивно, поэтому вам не нужно создавать их в куче (как в Java). Более правильный код будет выглядеть следующим образом:

#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)); является непосредственной утечкой памяти.

Сначала вы динамически выделяете Node, а затем копируете, что Node в вектор. Операция копирования - это то, что создает новый объект, поэтому this отличается.

Исходный (динамически выделенный) Node никогда не освобождается, но копии - это когда выполняется деструктор вектора (т.е. в конце функции).

Почему три вызова деструктора вместо двух? Это вызвано автоматическим перераспределением вектора, когда вы push_back. Он перемещает/копирует свои элементы в новое место памяти, уничтожая старые элементы.


Обратите внимание, что обычно, когда вам просто нужен вектор n построенных по умолчанию элементов, вы должны:

std::vector<Node> vec(2);

Это вызывает Node() (конструктор по умолчанию) для элементов vec[0] и vec[1], и вам не нужен цикл (или динамическое распределение).

Ответ 3

Когда вы добавляете элемент в вектор, вы создаете новую копию через конструктор copy (или конструктор перемещения).

Итак, строка типа vec.push_back( *(new Node)); выполняет две вещи.

  • Он создает новый Node динамически выделенный с помощью new.
  • Он копирует новый Node в вектор. Это означает, что новый конструктор Node создается через конструктор копирования.
  • Оригинальный Node никогда не освобождается (это называется утечкой памяти)

Эта версия вашего кода с конструктором копирования может предоставить вам некоторое представление: http://ideone.com/ow5YOI

Ответ 4

Ваш цикл эквивалентен

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

что происходит:

  • new Node выделяет память и создает Node (Node)
  • push_back выделяет новое хранилище
  • push_back создает объект Node в векторном хранилище, используя (неявный) конструктор копирования. (нет напечатанных сообщений)
  • Созданный Node просочился (существует 2 узла, 1 недоступен)
  • new Node выделяет память и создает Node (Node)
  • push_back выделяет новое хранилище и копирует/перемещает существующие элементы (не печатается сообщение)
  • push_back удаляет свое старое содержимое (Node удален)
  • push_back создает другой вектор Node в векторе, используя (неявный) конструктор копирования (не печатается сообщение)
  • Созданный Node просочился (существует 4 узла, 2 недоступны)

Когда вектор выходит из области видимости, две копии, которые он содержит, удаляются. (Node удален, Node удален)


Обычно вы пишете

vec.push_back(Node())

или

vec.emplace_back()

вместо.


Если вы хотите действительно понять, что происходит, вам следует создать конструктор копии, отличный от используемого по умолчанию:

Node(const Node& other){
    data = other.data;
    std::cout << "Node created. " << this
              << " from " << &other << std::endl;
}