面字节被问了这个问题,当时没有答上来,现在自己学习了一下做个总结
文章目录
一、求容器大小的方法
vector<int> vc; // 定义一个 int 类型的动态数组 vc
vc.size(); //返回当前 vc 元素的个数。
vc.capacity(); //返回当前 vc 中最大能够存储的元素的个数。(即容量)
二、扩容的方法
我们知道动态数组是可以动态地插入删除数据,但是动态数组也是预设了数组大小,在大小不够的时候,会申请一块更大的新内存,进行数组扩容。
起始容量为0,GCC 是 2 倍扩容,即之后插入按照 1 2 4 8 16 二倍扩容,VS13 是 1.5 倍扩容。扩容后是一片新的内存,需要把旧内存空间中的所有元素都拷贝进新内存空间中去,之后再在新内存空间中的原数据的后面继续进行插入构造新元素,并且同时释放旧内存空间,并且,由于 vector
空间的重新配置,导致旧 vector
的所有迭代器都失效了。
示例
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vc;
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
for(int i=0; i<17; ++i)
{
vc.push_back(i);
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
}
return 0;
}
三、resize 和 reserve 方法
除了插入数据会扩容,resize()
以及 reserve()
也会扩容动态数组。
以下摘自博客 (除示例)https://blog.csdn.net/rusbme/article/details/98102016
1. resize()
-
resize
方法被用来改变vector
中元素的数量,我们可以说,resize
方法改变了容器的大小,且创建了容器中的对象; -
如果
resize
中所指定的n小于vector
中当前的元素数量,则会删除vector
中多于n
的元素,使vector
得大小变为n
; -
如果所指定的
n
大于vector
中当前的元素数量,则会在vector
当前的尾部插入适量的元素,使得vector
的大小变为n
,在这里,如果为resize
方法指定了第二个参数,则会把后插入的元素值初始化为该指定值,如果没有为resize
指定第二个参数,则会把新插入的元素初始化为默认的初始值; -
如果
resize
所指定的n
不仅大于vector
中当前的元素数量,还大于vector
当前的capacity
容量值时,则会自动为vector
重新分配存储空间;
示例
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vc;
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
vc.resize(17);
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
vc.resize(32);
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
return 0;
}
2. reserve()
-
reserve
方法被用来重新分配vector
的容量大小; -
只有当所申请的容量大于
vector
的当前容量时才会重新为vector
分配存储空间;小于当前容量则没有影响 -
reserve
方法对于vector
元素大小没有任何影响,不创建对象。
示例
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vc;
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
vc.reserve(17);
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
vc.reserve(32);
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
return 0;
}
四、1.5倍扩容 vs 2倍扩容
假设释放后的空闲内存会合并
1. 2倍扩容
- 申请内存 16 KB,空闲内存 0 KB
- 申请内存 32 KB,空闲内存 16 KB
- 申请内存 64 KB,空闲内存 48 KB
- 申请内存 128 KB,空闲内存 112 KB
扩容因子为2时,上述例子表明:每次扩容,我们释放掉的内存连接起来的大小,都小于即将要分配的内存大小。
2. 1.5倍扩容
- 申请内存 16 KB,空闲内存 0 KB
- 申请内存 24 KB,空闲内存 16 KB
- 申请内存 36 KB,空闲内存 40 KB
- 申请内存 54 KB,空闲内存 76 KB
- 申请内存 81 KB,空闲内存 130 KB
- 申请内存 122 KB,空闲内存 8 KB
可以看出最后一次,申请的内存可以复用之前的空闲内存,这样就提高了内存的利用率。
五、容器的回收
clear()
和 erase()
方法都只会修改动态数组的尺寸大小即 size()
,而不会改变容量大小即 capacity()
,如果和另一个动态数组进行 swap()
操作,会改变 capacity()
。
示例
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vc;
for(int i=0; i<30; ++i)
{
vc.push_back(i);
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
}
cout<<"-----------------------------------"<<endl;
vc.clear();
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
vc.erase(vc.begin(), vc.end());
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
vector<int>().swap(vc);
cout<<"size:"<<vc.size()<<" capacity:"<<vc.capacity()<<endl;
return 0;
}
六、push_back 和 emplace_back的区别
push_back
和 emplace_back
最大的区别就是 emplace_back
支持直接传入构造函数需要的参数,在动态数组中直接构造这个对象。
我们用一个case来说明,考虑有下面 Person
类
class Person
{
public:
Person(string name, int age)
{
this->name = name;
this->age = age;
cout << "调用构造函数" << endl;
}
Person(const Person& other)
{
this->name = other.name;
this->age = other.age;
cout << "调用拷贝构造函数" << endl;
}
Person(Person&& other) noexcept
{
this->name = other.name;
this->age = other.age;
cout << "调用移动构造函数" << endl;
}
~Person()
{
cout << "调用析构函数" << endl;
}
string name;
int age;
};
然后我们有一个动态数组
vector<Person> persons;
如果我们传入一个左值
Person person("hhhcbw", 24);
persons.push_back(person);
//persons.emplace_back(person);
它们的结果都是一样的
如果我们传入一个右值
Person person("hhhcbw", 24);
persons.push_back(std::move(person));
//persons.emplace_back(std::move(person));
它们的结果也是一样的
但是 emplace_back
支持直接传入参数在动态数组中直接构造,而 push_back
不支持
persons.emplace_back("hhhcbw", 24 );
输出如下