STL向量的内存分配

3
我在想为什么向量模板要执行两次分配,当只需要一次时就可以了。
例如这个:
#include <vector>
#include <iostream>

class A {
    public:
         A(const A &a) {
            std::cout << "Calling copy constructor " << this << " " << &a << "\n";
        }
         A() {
            std::cout << "Calling default constructor " << this << "\n";
        }
        ~A() {
            std::cout << "Calling destructor " << this << "\n"; 
        }
};

int main(int argc, char **argv)
{
    std::vector <A> Avec;

    std::cout << "resize start\n";
    Avec.resize(1);
    std::cout << "resize end\n";

    return 0;
}

输出:

开始调整大小
调用默认构造函数 0x7fff9a34191f
调用复制构造函数 0x1569010 0x7fff9a34191f
调用析构函数 0x7fff9a34191f
调整大小结束
3个回答

15

它并没有执行两次分配,而是创建一个默认构造函数生成的对象以便调用resize函数,然后将该对象复制到新位置,最后销毁该参数。

如果您查看resize的参数:

void resize(n, t = T())

它的默认参数是类型为T的默认构造对象(这是您输出中调用的默认构造函数)。然后,在函数内部,它将其复制到正确的位置(这是复制构造函数)。在resize函数结束后,销毁该参数(输出中的析构函数调用)。


0

我认为这将会进行两次分配:
  • 一次是为了新的 A()
  • 一次是为了 push_back() 的副本
- ynimous
@ynimous:不完全是这样,上面的代码改变了语义,使得向量包含指针。它只会创建一个A元素,而会创建并复制指针(这可能更快)。 - David Rodríguez - dribeas
1
这种方法的问题在于你正在改变代码的语义。在向量内部,对象以值的形式保存,而向量的销毁意味着所有包含元素的销毁。现在,由于元素是通过指针保存的,因此必须调整代码,并且用户必须手动销毁所有资源(A对象)。虽然你减少了A构造的数量,但代码现在更加脆弱,容易出现资源泄漏。 - David Rodríguez - dribeas

0

这里有一个猜测:

  1. 编译器重新排列了Avec的初始分配,直到“调整大小开始”之后。
  2. 向量最初分配为0个元素。
  3. 调整大小会用默认的A填充新元素(通过创建默认的A,将其复制到向量中,并删除临时对象来实现)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接