为什么vector的方法调整大小会创建其他对象?

时间:2019-12-05 23:06:54

标签: c++ vector

根据我的理解,模板类resize的{​​{1}}方法使用不带参数的构造函数来创建新对象,然后使用复制构造函数来克隆先前的对象。确实,此代码证明了这一点:

vector<class>

输出与#include <iostream> #include <vector> using namespace std; class A{ public: A(){ cout << "Appel constructeur !" << endl; cout << this << endl; } A(const A &a){ cout << "Appel constructeur de recopie" << endl; cout << this << endl; } ~A(){ cout << "Appel destructeur !" << endl; } }; int main() { vector<A> t; t.resize(2); cout << t.size() << endl; cout << &t[0] << endl; cout << &t[1] << endl; }

mingw32-g++.exe

带有Appel constructeur ! 0x69fedf Appel constructeur de recopie 0x905690 Appel constructeur de recopie 0x905691 Appel destructeur ! 2 0x905690 0x905691 Appel destructeur ! Appel destructeur ! 的输出(它两次调用构造函数)

g++

所以我的问题是:为什么要创建一个新对象然后销毁它?为什么创建的第一个对象的地址与其他对象相距很远?

2 个答案:

答案 0 :(得分:0)

您的编译器正在为您提供的代码做一些奇怪的事情,我只能假设这是因为您使用的是C ++ 11之前的版本。从cppreference开始,在C ++ 11之前的resize方法将默认的构造对象作为第二个参数(如果未指定)。这将导致对构造函数的调用。

  

如果当前大小小于count,则会附加附加元素并使用value的副本进行初始化。

这意味着默认的构造对象将被复制到向量中分配的两个新位置;这就是您在mingw中看到的

在C ++ 11和更高版本中,它应该只调用一次构造函数,两次调用析构函数,不涉及任何副本。调用t.resize(2)时,会将两个“默认插入”对象添加到容器中,这些对象将调用构造函数。当向量超出范围并被销毁时,将调用析构函数。

如果您改为执行以下操作:

t.resize(2);
t.resize(5);

现在,当且仅当重新分配内部数组时,您才会获得一些副本或在第二次调整大小时移动。前两个元素将被复制或从旧数组移动到新数组。最后3个元素将默认插入到新数组中。

关于对象的内存地址差异很大,这实际上就是计算机中的工作方式。初始数组分配在动态内存的一个位置。调整大小后将获得一个新的内存位置,该位置可能紧邻旧位置,也可能完全位于其他位置。

Here is a demonstration同时具有C ++ 17和C ++ 11。

答案 1 :(得分:0)

  

vector使用不带​​参数的构造函数来创建新对象,然后使用复制构造函数来克隆先前的对象。确实,此代码证明了这一点:

代码输出不能证明任何事情,只能显示一种特定编译器在一种特定情况下所做的事情。

在C ++ 11之前,该标准指定新元素的创建就像是通过insert从值初始化的临时副本中进行复制构造一样。因此地址不同,因为矢量中的对象位于动态存储中,而临时对象位于临时(或自动)存储中。

自C ++ 11起,它必须使用 default-insertion 创建新对象,该对象可以归结为默认分配器的值初始化。

您的结果很可能是通过使用旧的编译器(或在较新的编译器上不使用C ++ 11模式)解释的。