向量的记忆行为问题

Questions on memorybehavior of vectors

本文关键字:问题 记忆 向量      更新时间:2023-10-16

最近我对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;
}

在我的理解中,向量及其内容存在于堆栈中,堆栈(最终(会被时间覆盖,而我返回的向量及其内容将毫无用处。或者它实际上返回了向量的副本及其内容的副本(如果内容数据类型不是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;
}

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

此示例:

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

CCD_ 12的结果CCD_。return foov;可以调用std::vector<foo_t>的副本生成器,并且它可以自由不进行该副本,请参阅副本省略

无论如何,从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()中(

foov.push_back(foo);

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

return foov;

编译器可以使用返回值优化对此进行优化。以我在coliru上运行的这个短程序为例。请参阅此问题中的其他优秀答案。

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

现在我必须手动地覆盖向量,删除它的内容,然后我可以安全地让向量脱离范围,对吧?

是的。出于同样的原因

int* a = new int();

a死亡时不会delete a;

因此,当我离开函数时,向量会被释放内容(这里是一个foo_t(也会被释放吗?

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

或者它真的返回了向量的副本contents(如果其不是POD(?

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

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

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