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
Почему существует дополнительный разрушение и почему создаются объекты, отличные от уничтоженного объекта?
Ответы
Ответ 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;
}