为什么连续的向量:: push_back将导致不同数量的构造函数调用
Why successive vector::push_back results into different number of constructor call?
class base
{
private:
int k;
public:
base(const base& b){ this->k = b.k; cout<<" c-ctor "<<endl; }
base(int a = 10){ k = a; }
~base(){cout << "destructor calledn";}
};
int main()
{
base b, b1(2);
vector<base> m;
cout << "first pushback" <<endl;
m.push_back(b);
cout << "2nd pushback" <<endl;
m.push_back(b1);
cout << "3rd pushback" <<endl;
m.push_back(b1);
cout << "4th pushback" <<endl;
m.push_back(b);
cout << "5th pushback" <<endl;
m.push_back(b);
cout<<" =============================================== "<<endl;
return 0;
}
输出:
first pushback
c-ctor
2nd pushback
c-ctor
c-ctor
destructor called
3rd pushback
c-ctor
c-ctor
c-ctor
destructor called
destructor called
4th pushback
c-ctor
5th pushback
c-ctor
c-ctor
c-ctor
c-ctor
c-ctor
destructor called
destructor called
destructor called
destructor called
===============================================
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
为什么 i th push_back
导致 i 复制构造函数的数量?
不是调整大小效果(即再次复制原始向量),而是将元素插入向量的效率低下的方式?
为什么 4 Th push_back
的行为与 2 th , 3 th abd 5 th push_back
?
demo
没什么大不了的。每次size
达到其capacity
时,都将向量重新分配。所有元素均从旧向量复制到新向量。
通常,将原始容量分配给新向量的两倍。
- 在第1
push_back
之前,容量为1。因此,它不需要重新分配。 - 对于第二个
push_back
,容量需要加倍,因此进行了两个复制构造函数的调用,首先将旧元素复制到新的矢量,第二个用于push_back
。现在的容量为2。 - 第三
push_back
再次需要重新分配向量,因为现在的容量为2。重新分配能力变为4。 - 现在没有进行重新分配,因此只有一个呼叫复制CTOR(对于
push_back
)。容量仍然是4。 - 对于第5个
push_back
,将进行重新定位,并将4个旧元素和一个新元素(push_back
)复制到新向量。现在的容量是8。
如果您进一步继续,您会观察到重新分配将发生在9th push_back
。
此外,在重新分配时需要调用攻击器,而当不再需要较旧的向量并应销毁其成员时。
std::vector
可以看作是动态数组。因此,它在需要时会生长,这需要向量分配更多内存, copy 向量中的现有元素与新的较大内存。
然后破坏了旧内存中的现有对象。
如果您事先知道向量中需要多少个元素,则可以保留存储器的内存。通过执行此操作,向量无需重新分配和复制数据,直到您达到新容量为止。
我真的建议您阅读有关std::vector
的更多信息,尤其是有关其及其 size 的差异。ANS">
向量需要扩展以适应新元素。需要将现有元素复制到新的缓冲区中。
如果您的班级是noexcept
移动可构造,则它将生成对移动构造函数的呼叫。
并非每个push_back
都会导致矢量重新定位内存的原因,这可能是由于您的标准库实现试图在调整大小后保留一些额外的内存,以免经常进行操作。这是在vector
实施的自由度之内,并且有几种策略来确定要保留多少。
退后一步是STD ::向量的关键要求:向量中的元素必须连续存储。这意味着您可以安全地将向量中元素的指针用作指向该类型元素的原始指针,因此可以按照固定尺寸的数组来使用数组语义和指针算术。
这也意味着通过其索引访问数组的任何元素非常快,并且无论数组的大小如何,都需要相同的时间,以及您要访问的特定元素的索引(这称为一个称为o(1)或"恒定时间"操作)。
需要为此收益支付的罚款是重新分配。创建向量时,您通常不知道要使用多少元素,因此您分配了一些连续的内存来存储一些元素。如果您不断添加到向量,则将超过此初始分配。但是,您不能仅仅扩大向量使用的内存量,因为您的程序可能已将当前矢量后的内存立即分配给其他变量。确保向量元素保持连续的唯一方法是分配一个足够大的新块以包含所有元素,包括附加元素,然后将所有元素复制到此新位置。
正如其他人所指出的那样,每次超过容量时,通过分配1.5或2倍的原始阵列大小的1.5或2倍,将数组尺寸成倍增加。这减少了整个数组在生长时需要重新分配的方式。
相反,如果您想要一个元素的集合,其中它总是相对较快地添加元素(或者至少始终需要相同的时间),则可以使用链接列表(std :: list),但然后您不能再通过索引快速访问任何元素("随机访问")。
"将元素插入向量的效率低下"
正如其他人指出的那样,您可以提示std::vector
在首先为您保留足够的内存,以允许将项目推到您的矢量上,直至此限制,必须重新分配。如果更改代码将以下调用添加到reserve
:
vector<base> m;
m.reserve(5);
cout << "first pushback" <<endl;
使用reserve
不会更改向量的 size ,但它将分配足够的内存以保持该大小的连续向量,从而避免重新分配。在您的示例中,您会看到被调用的复制构造函数和破坏者的数量大大减少。如果您知道(大致甚至)矢量的大小和类型的构建/破坏是昂贵的,那么这可能会对您的代码产生很大的影响。
请注意,reserve
与resize
有很大不同 - 当您将项目推到矢量(您的示例)时,第一个最有用的是,第二个将创建一个填充的项目,并运行复制构造函数n次;随后通过下标运算符访问和更改项目时,这很有用。
- 变量没有改变?通过向量的函数调用
- 在具有向量的类构造函数中进行析构函数调用
- 调用向量的函数,给定向量的指针作为 void *
- 析构函数调用c++中的一个向量
- 在不同的线程中调用向量析构函数或清除
- 具有多线程支持的 RenderClass,将函数调用推送到向量以在另一个线程上调用
- 用构造函数调用填充向量
- 调用向量没有匹配函数
- 为什么noexcept move构造函数在向量重新分配期间没有被调用
- 调用向量内的函数指针不执行任何操作
- 为什么连续的向量:: push_back将导致不同数量的构造函数调用
- 有没有办法<int><double>在使用 C++11 函数调用期间自动将"向量"提升为"向量"?
- c++oop初学者-在一个函数调用中返回向量中每个创建对象的输出和
- 直接在函数调用的参数列表中初始化向量
- Visual Studio报告对结构向量的push_back()函数调用出错
- c++没有用于调用向量push_back的匹配函数
- 从函数返回向量<向量<int>>是否会调用 C++11 中的任何移动构造函数
- C++从函数调用的多个返回中构建字符串向量的最佳方法
- 推入C++向量时的构造函数和析构函数调用
- 为模板类创建一个构造函数,该构造函数在向量中调用模板初始化