vector是std命名空间的一个容器,我们可能会经常使用到,但是不适当的使用可能会使得程序很糟糕,或者说程序的性能差。
那么我们如何高效的使用vector呢? 首先我们先来分析vector内部如何实现内存动态开辟的。
问题一:
我们往vector中存放数据的时候,如果vector的空间不够了,并且其后面的内存已经被占用,那么它就会在内存的另外一个位置,重新开辟一块足够存放数据的内存(大部分时候都是这样的),然后将以前内存中的数据拷贝到新内存,然后将新增数据也放到内存中,最后将以前的旧内存释放带掉。
从上面vector动态开辟空间的过程,我们可以看到如果空间不够,它就会重新开辟,然后将旧数据拷贝到新位置,这个拷贝数据的过程会很浪费时间,增加开销。也会导致我们使用vector的时候会很慢。(因为你数据很多,每次都将之前的数据拷贝到新位置,这很明显有很大的开销)
解决方案:
上面说到,vector在内存不够的时候才会重新开辟空间,才会发生数据的拷贝。那如果我们一次性开辟足够的空间,这样存放数据的时候就尽可能的去避免重新开辟空间,避免发生数据的拷贝。
所以我们需要提前指定vector开辟相应的空间。主要有两种方式:
方式一: 通过有参构造
vector<T> v1(num);
vector<T> v1(num,elem);
方式二: 通过reserve()函数 (推荐)vector<T> v1;
v1.reserve(10);
使用reserve()应该注意其实预留空间而不是开辟空间。reserve注意事项
我们选择方式二,因为方式一虽然会开辟空间,但是会往内部放一个默认数据,这也会增加开销,因为我们目前只是希望开辟足够的空间来存放之后要存放的数据。
如果最后数据存放结束,有多余的空间我们可以使用shrink_to_fit()函数将多余的内存删除
问题二:
解决了空间扩充时的消耗,但是我们往vector的内部存放数据的时候也会存在消耗。
我们以一个类对象作为例子(其余的都类似)。v1.push_back(Student()); // 往v1中放一个Student的对象。
首先这段代码,会先创建一个Student()的对象在函数的堆栈内存上,然后将该内存的数据存放到vector管理的内存上,这就要将堆栈位置的数据拷贝到vector对应的内存。(会调用拷贝构造函数)
会发现,如果我们存放10个对象,就会调用10次拷贝构造,这很明显也是会影响性能的。
push_back()和insert()一样
解决方案:
我们可以使用c++11提供的emplace和emplace_back()。
这两个函数接收的参数并不是我们要存储的对象,而是构建对象所用的数据。(也就是对应构造函数的参数)
其不会在函数的堆栈内存中创建对象,而是直接在vector管理的空间上使用传入的参数,调用对应的构造函数,创建对象。
这样,我们就直接在vector的内存上创建了对象,就省去了之前拷贝的过程。