今日诗词:
爱他明月好,憔悴也相关。
西风多少恨,吹不散眉弯。
——《临江仙·寒柳》【清】纳兰容若
目录
string中的和vector中的capacity有什么区别
引言:
看标题就知道我们今天要说的是capacity。其实我们之前在讲关于C++的(动态,静态)二维数组的补充那一章就提到过capacity,只不过当时是string的capacity,今天说的是vector中的capacity。那么两者的关系是增么样的,以及capacity的扩容规则是什么,还有就是在vector数组中的体现是什么,请看正文。
正文
string中的和vector中的capacity有什么区别
vector中的capacity和string中的capacity在概念上有相似之处,但也存在一些差异,主要体现在它们各自所属的数据结构和用途上。
1.共同点
容量定义:两者都表示容器在不重新分配内存的情况下能够存储的最大元素数量。即,capacity是容器预留的空间大小,用于存储元素,但并非所有预留的空间都已被实际使用。
获取方式:通过各自的成员函数capacity()来获取当前的容量值。
差异点
2.数据结构:
vector是C++标准模板库(STL)中的一个序列容器,用于存储元素的集合,这些元素可以是任意类型,并支持随机访问。
string同样是STL中的一个容器,但它专门用于存储字符序列,通常用于处理文本数据。在内部实现上,string可以被视为一种特殊的vector<char>,但它在操作字符序列时提供了更多的便利性和安全性。
3.扩容行为:
当vector的当前大小(size)等于其容量(capacity)时,如果继续添加新元素,vector会自动扩容,通常是通过分配一个更大的内存块并将旧元素复制到新块中来实现的。扩容的具体策略(如增长因子)可能因编译器和标准库实现而异。
string的扩容行为与vector类似,但在处理字符序列时可能有一些特定的优化。例如,某些string实现可能会使用短字符串优化(SSO),即对于较短的字符串,直接使用对象内部的存储空间来避免动态内存分配。
4.用途和特性:
vector因其灵活性和随机访问能力而被广泛用于需要动态数组的场景。
string则专门用于处理文本数据,提供了丰富的字符串操作函数,如拼接、查找、替换等,这些操作在vector中可能不那么直观或高效。
5.总结:
vector中的capacity和string中的capacity都表示容器预留的空间大小,用于存储元素。它们在概念上是相似的,但在具体的数据结构、扩容行为和用途上存在差异。vector更侧重于提供一个灵活的动态数组,而string则专注于高效地处理文本数据。
vector扩容时内存分配的策略是什么?
vector扩容时内存分配的策略主要是基于其当前容量(capacity)和元素数量(size)来决定的,具体策略可能因不同的编译器和标准库实现而有所不同,但通常遵循以下原则:
1.增长因子:
当vector的当前容量不足以容纳新元素时,它会重新分配一块更大的内存空间。这块新空间的大小通常是原容量的一个倍数,这个倍数就是所谓的增长因子。常见的增长因子是2,意味着新容量通常是原容量的两倍。但也有其他可能的增长因子,如1.5倍,这取决于具体的实现和操作系统。
2.内存分配:
vector会分配一块足够大的新内存块,其大小至少能容纳当前所有元素加上即将添加的新元素。这块新内存块的大小由增长因子和当前容量共同决定。
3.元素复制/移动:
在分配了新的内存块之后,vector会将原内存块中的所有元素复制到新内存块中。这个复制过程可能通过元素的拷贝构造函数或移动构造函数来完成,具体取决于元素的类型和编译器的优化。
4.内存释放:
在元素复制完成后,vector会释放原来的内存块,以避免内存泄漏。
5.更新指针和容量:
最后,vector会更新其内部指针,使其指向新的内存块,并更新其容量值以反映新的内存块大小。
6.需要注意的是,虽然增长因子通常是2,但这不是一个固定的规则。不同的编译器和标准库实现可能会有不同的增长策略。此外,由于内存分配和释放的开销,频繁地扩容可能会对性能产生负面影响。因此,在可能的情况下,建议使用reserve()成员函数来预先分配足够的内存空间,以减少扩容的次数并提高程序的性能。总的来说,vector扩容时内存分配的策略是灵活的,旨在平衡内存使用效率和程序性能之间的关系。
capacity在vector中的表现如何
这里我们依然借用一下上期内容的代码来说明:C++的动态数组vector
include<iostream>
include<vector>
using namespace std;
int main()
{
//创建动态数组
vector<int>v={1,2,3,4};
cout<<"size is"<<v.size()<<endl;
cout<<"capacity is"<<v.capacity()<<endl;
//遍历所有元素
for(int i=0;i<v.size();++i)
{
cout<<v[i]<<endl;
}
//在尾部插入一个元素
v.push_back(5);
cout<<"size is"<<v.size()<<endl;
cout<<"capacity is"<<v.capacity()<<endl;
//遍历所有元素
for(int i=0;i<v.size();++i)
{
cout<<v[i]<<endl;
}
//在尾部任意位置插入一个元素
v.insert(--v.end(),6);
cout<<"size is"<<v.size()<<endl;
cout<<"capacity is"<<v.capacity()<<endl;
//遍历所有元素
for(int i=0;i<v.size();++i)
{
cout<<v[i]<<endl;
}
//在尾部移除一个元素
v.pop_back();
cout<<"size is"<<v.size()<<endl;
cout<<"capacity is"<<v.capacity()<<endl;
//遍历所有元素
for(int i=0;i<v.size();++i)
{
cout<<v[i]<<endl;
}
//在尾部移除一个元素
v.erase(v.end()-1);
cout<<"size is"<<v.size()<<endl;
cout<<"capacity is"<<v.capacity()<<endl;
//遍历所有元素
for(int i=0;i<v.size();++i)
{
cout<<v[i]<<endl;
}
return 0;
}
大家看上面的两张黑框框,这个就是cout的窗口,我们现在对照着代码和图片的结果观察。
解析:
一开始我们定义的vector数组的内容大小是4,预存储的大小也是4,这是因为我们的初始化的影响。然后我们在尾部添加一个元素,这是再看内容大小是5,预存储大小是6。这是因为我们添加了一个元素,导致vector数组自动的扩容,从而预存储大小变大。我们上面说到vector的扩容是按倍数来扩容的,这里的6就是4的1.5倍大小。然后我们在数组任意位置再插入一个数组,这时我们的内容大小变大到6,预存储大小不变(因为·够用,所以不会再变化)。之后的连着两次的删除元素都只会导致内容的大小变化(变小),预存储的大小不变,这是因为编译器以防万一,害怕内存不够用的机制。
这就是vector中的capacity的各种方面的体现。我们只有理解了这些基本的原理才会让我们在以后的C++道路上越来越通畅。
🆗到这里,这篇关于C++的动态数组vector番外之capacity就说完了,求一个免费的赞,感谢阅读,我们下期见。