我试图谷歌搜索我的问题的答案,但我找不到任何有效的解释,因此我在这里发布我的问题。以下是我的示例代码和输出:
#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
为什么会有额外的破坏以及为什么创建的对象与被破坏的对象不同?
答案 0 :(得分:39)
vec.push_back( *(new Node));
是即时内存泄漏。
首先,您动态分配Node
,然后将<{strong>} Node
复制到向量中。复制操作是创建新对象的原因,这就是this
不同的原因。
原始(动态分配的)Node
永远不会被释放,但副本是在向量的析构函数运行时(即在函数的末尾)。
为什么三个析构函数调用而不是两个?这是由push_back
时向量的自动重新分配引起的。它将其元素移动/复制到新的内存位置,从而破坏旧元素。
请注意,通常情况下,当您只需要n
默认构造元素的向量时,您可以执行以下操作:
std::vector<Node> vec(2);
这会调用元素Node()
和vec[0]
的{{1}}(默认构造函数),并且您不需要循环(或动态分配)。
答案 1 :(得分:18)
如果您要添加复制构造函数,您会找到答案:
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
因此,当您向向量添加第二个元素时,将没有足够的容量,向量必须调整存储大小并将所有旧元素(在这种情况下只有一个这样的元素)复制到新空间。这就是为什么要调用一个额外的拷贝构造函数的原因。
是的,在C ++中,所有标准容器都要求对象应该复制或移动构造,因此您不必在堆上创建它们(如在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 :(得分:12)
向向量添加元素时,可以通过复制构造函数(或移动构造函数)构建新副本。
所以像vec.push_back( *(new Node));
这样的行会做两件事。
new
构建一个动态分配的新节点。此版本的代码与复制构造函数可能会为您提供一些见解:http://ideone.com/ow5YOI
答案 3 :(得分:4)
你的循环等同于
vec.push_back( *(new Node));
vec.push_back( *(new Node));
会发生什么:
new Node
分配内存并创建节点(创建节点)push_back
分配新存储空间push_back
使用(隐式)复制构造函数在向量的存储中创建一个Node
对象。 (没有打印信息)Node
已泄露(存在2个节点,1个无法访问)new Node
分配内存并创建节点(创建节点)push_back
分配新存储空间,复制/移动现有元素(不打印任何消息)push_back
删除旧内容(节点已删除)push_back
使用(隐式)复制构造函数(未打印任何消息)在向量中创建另一个Node
Node
已泄露(存在4个节点,2个无法访问)当向量超出范围时,它包含的两个副本将被删除。 (节点已删除,节点已删除)
你通常会写
vec.push_back(Node())
或
vec.emplace_back()
代替。
如果你想真正了解发生了什么,你应该创建一个非默认的复制构造函数:
Node(const Node& other){
data = other.data;
std::cout << "Node created. " << this
<< " from " << &other << std::endl;
}