关于向量记忆行为的问题

时间:2016-11-01 09:59:32

标签: c++ c++11 stl stdvector

我最近对std::vectors

的内存(de)分配感到困惑

让我们假设我得到了正整向量的整数: 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;
}

在我的理解中,向量及其内容存在于堆栈中,它最终被时间覆盖并且我返回的向量及其内容将是无用的。或者它实际上是否返回了带有其内容副本的向量副本(如果它不是POD,则需要内容数据类型的Copy-Constructor)?

显而易见的事情:

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

现在我必须手动迭代向量,删除其内容,然后我可以安全地让向量超出范围,对吗?

3 个答案:

答案 0 :(得分:3)

这个例子:

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()后,foovfoo都会被释放。

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

std::vector<foo_t>的结果hurr()有效,其中包含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()

答案 1 :(得分:3)

foov.push_back(foo);

实际上,您构建了一个foo_v并将其推回,实际上创建了一个新的foo_v,并将foov作为参数称为复制构造函数。如果您想避免这种情况,请使用emplace_back

return foov;

编译器可以使用返回值优化来优化它。请参阅我在coliru上运行的this short program作为示例。请参阅this question中的其他优秀答案。

std::vector<foo_t*> foov;
/* add elements to foov with new */
  

现在我必须手动迭代向量,删除其内容,然后我可以安全地让向量超出范围,对吗?

是的,你这样做。出于同样的原因

int* a = new int();

delete a;去世时不会a

答案 2 :(得分:2)

  

因此,当我离开函数时,向量将被释放并且   内容(这里有一个foo_t)也被取消分配了吗?

是。如果std::vector有非平凡的析构函数,它就会被调用。

  

或者它实际上是否返回了带有副本的向量副本   内容(如果是内容数据类型,则需要一个Copy-Constructor   不是POD)?

是的,在这种情况下,它会返回一份副本。现代编译器可能会为{{1}}调用复制构造函数,而后者又会为每个元素调用包含类型的复制构造函数。 C ++ 17引入了保证返回值优化(RVO),因此不会调用向量的复制构造函数。不过,如果设置高优化级别,现代编译器也可以使用RVO。

  

现在我必须手动迭代向量,删除其内容   然后我可以安全地让矢量超出范围,对吗?

是的,你是对的。如果您不想手动迭代,请考虑使用智能指针。