关于向量记忆行为的问题
Questions on memorybehavior of vectors
最近我对 std::vectors
的内存(取消)分配有点困惑
假设我得到了整数的法线向量:
std::vector<int> intv;
当我 push_back
一些 int
时,它会随着时间的推移而增长。当我离开函数的范围(即)时,它会在不需要额外调用的情况下被释放。
太棒了。让我们再举一个例子:
struct foo_t{
std::string bar:
unsigned int derp;
}
void hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
}
好的。当我调用 hurr()
时,向量被创建,一个 foo_t
实例被创建,实例被填充并被推送到向量。所以当我离开函数时,向量被释放,内容(这里是一个foo_t
)也被释放?
下一个例子:
struct foo_t{
std::string bar:
unsigned int derp;
}
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
return foov;
}
根据我的理解,向量及其内容存在于堆栈中,堆栈(最终)会被时间覆盖,我返回的向量及其内容将无用。或者它实际上是 returns 向量的副本及其内容的副本(如果它不是 POD,则需要内容数据类型的复制构造函数)?
还有一些显而易见的事情:
struct foo_t{
std::string bar:
unsigned int derp;
}
std::vector<foo_t*> hurr(){
std::vector<foo_t*> foov;
foo_t foo = new foo_t;
foo->bar = "Sup?";
foo->derp = 1337;
foov.push_back(foo);
return foov;
}
现在我必须手动迭代向量,删除它的内容,然后我才能安全地让向量超出范围,对吗?
这个例子:
struct foo_t{
std::string bar;
unsigned int derp;
};
void hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
}
在 hurv()
完成后,foov
和 foo
都被释放了。
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
return foov;
}
hurr()
的结果std::vector<foo_t>
有效,其中1个foo_t
有效。
return foov;
可以 调用 std::vector<foo_t>
的复制构造函数,它可以自由地不进行复制,参见 copy elision
无论如何,从C++11开始,你可以这样写:
struct foo_t{
std::string bar;
unsigned int derp;
// we will copy the string anyway, pass-by-value
foo_t(std::string bar_, unsigned int d_)
: bar(std::move(bar_)), derp(d_) {}
};
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
// This is better, in place construction, no temporary
foov.emplace_back("Sup?", 1337);
// This require a temporary
foov.push_back(foo_t{"Sup?", 1337});
return foov;
}
并且,对于最后一个示例,是的,您 必须手动遍历向量,删除其内容然后我可以安全地让向量超出范围 当您不想再使用 hurr()
的结果,(不在 hurr()
)
So when I leave the function, the vector gets deallocated and the
content (here one foo_t
) gets deallocated, too?
是的。如果 foo_t
有非平凡的析构函数,它将被调用。
Or does it actually returns a copy of the vector with a copy of its
contents (requires a Copy-Constructor for the content datatype if its
not a POD)?
是的,在本例中它 return 是一个副本。现代编译器可能会为 std::vector
调用复制构造函数,这反过来又会为每个元素调用包含的类型的复制构造函数。 C++17 引入了保证 return 值优化 (RVO),因此不会调用向量的复制构造函数。不过,如果您设置高优化级别,现代编译器也可能会使用 RVO。
Now I have to manually iterate over the vector, delete its contents
and then I can safely let the vector fall out of scope, right?
是的,你是对的。如果您不想手动迭代,请考虑使用智能指针。
foov.push_back(foo);
实际上,您构造了一个 foo_v
并将其推回,这实际上创建了一个新的 foo_v
并以 foov
作为参数调用了复制构造函数。如果您想避免这种情况,请使用 。
return foov;
编译器可以使用 return 值优化来优化它。参见 this short program I've made running on coliru as an example. Refer to the other excellent answers in this question。
std::vector<foo_t*> foov;
/* add elements to foov with new */
Now I have to manually itterate over the vector, delete its contents and then I can safely let the vector fall out of scope, right?
是的,你知道。同样的原因
int* a = new int();
当 a
死亡时不会 delete a;
。
最近我对 std::vectors
假设我得到了整数的法线向量:
std::vector<int> intv;
当我 push_back
一些 int
时,它会随着时间的推移而增长。当我离开函数的范围(即)时,它会在不需要额外调用的情况下被释放。
太棒了。让我们再举一个例子:
struct foo_t{
std::string bar:
unsigned int derp;
}
void hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
}
好的。当我调用 hurr()
时,向量被创建,一个 foo_t
实例被创建,实例被填充并被推送到向量。所以当我离开函数时,向量被释放,内容(这里是一个foo_t
)也被释放?
下一个例子:
struct foo_t{
std::string bar:
unsigned int derp;
}
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
return foov;
}
根据我的理解,向量及其内容存在于堆栈中,堆栈(最终)会被时间覆盖,我返回的向量及其内容将无用。或者它实际上是 returns 向量的副本及其内容的副本(如果它不是 POD,则需要内容数据类型的复制构造函数)?
还有一些显而易见的事情:
struct foo_t{
std::string bar:
unsigned int derp;
}
std::vector<foo_t*> hurr(){
std::vector<foo_t*> foov;
foo_t foo = new foo_t;
foo->bar = "Sup?";
foo->derp = 1337;
foov.push_back(foo);
return foov;
}
现在我必须手动迭代向量,删除它的内容,然后我才能安全地让向量超出范围,对吗?
这个例子:
struct foo_t{
std::string bar;
unsigned int derp;
};
void hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
}
在 hurv()
完成后,foov
和 foo
都被释放了。
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
return foov;
}
hurr()
的结果std::vector<foo_t>
有效,其中1个foo_t
有效。
return foov;
可以 调用 std::vector<foo_t>
的复制构造函数,它可以自由地不进行复制,参见 copy elision
无论如何,从C++11开始,你可以这样写:
struct foo_t{
std::string bar;
unsigned int derp;
// we will copy the string anyway, pass-by-value
foo_t(std::string bar_, unsigned int d_)
: bar(std::move(bar_)), derp(d_) {}
};
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
// This is better, in place construction, no temporary
foov.emplace_back("Sup?", 1337);
// This require a temporary
foov.push_back(foo_t{"Sup?", 1337});
return foov;
}
并且,对于最后一个示例,是的,您 必须手动遍历向量,删除其内容然后我可以安全地让向量超出范围 当您不想再使用 hurr()
的结果,(不在 hurr()
)
So when I leave the function, the vector gets deallocated and the content (here one
foo_t
) gets deallocated, too?
是的。如果 foo_t
有非平凡的析构函数,它将被调用。
Or does it actually returns a copy of the vector with a copy of its contents (requires a Copy-Constructor for the content datatype if its not a POD)?
是的,在本例中它 return 是一个副本。现代编译器可能会为 std::vector
调用复制构造函数,这反过来又会为每个元素调用包含的类型的复制构造函数。 C++17 引入了保证 return 值优化 (RVO),因此不会调用向量的复制构造函数。不过,如果您设置高优化级别,现代编译器也可能会使用 RVO。
Now I have to manually iterate over the vector, delete its contents and then I can safely let the vector fall out of scope, right?
是的,你是对的。如果您不想手动迭代,请考虑使用智能指针。
foov.push_back(foo);
实际上,您构造了一个 foo_v
并将其推回,这实际上创建了一个新的 foo_v
并以 foov
作为参数调用了复制构造函数。如果您想避免这种情况,请使用
return foov;
编译器可以使用 return 值优化来优化它。参见 this short program I've made running on coliru as an example. Refer to the other excellent answers in this question。
std::vector<foo_t*> foov;
/* add elements to foov with new */
Now I have to manually itterate over the vector, delete its contents and then I can safely let the vector fall out of scope, right?
是的,你知道。同样的原因
int* a = new int();
当 a
死亡时不会 delete a;
。