首先解释一下容量/大小的区别:
std::vector::capacity() : 指容器的能容纳多少个。
std::vector::size() : 指容器当前已装多少个。
明白上面的意思思路就清晰多了。
这几天有同事用vector的时候遇到问题,说是当push_back以后,以前引用了vector的地址就不能访问。
造成以前引用的地址不能使用的原因估计大家都知道。
主要原因是vector有一个内存增量机制。
内增量机制是:为了满路容器的连续内存访问,当vector发现自己内存不够用,就需要重新申请内存,
然后复制旧的数据过去,删除旧的数据,重新指向新的数据地址。
当这个复制过程一结束的时候,以前引用了下标为0的vector内存地址就会不再是该数据地址了,
已被复制走的原有数据已删除成为野内存。
这时需要重去引用一次。
关于这个增量机制网上有多种说法,
1说法:按原有尺寸的2倍增长,即:new_capacity = capacity_size * 2;
2说法:按原有尺寸的50%增加,即:new_capacity = capacity_size / 2 + capacity_size;
3说法:按数据尺寸倍数增长,即 sizeof(class) * 2;
4说法:按字节来,即: (1024)/sizeof(class);
就此问题,我专门去看了看vector的原代码。得到的结果是:2说法正确。
这点可以从std::vector::_Insert_n(iterator, size_type, const _Ty&)的原代码中可以看出:
// 如果容量 < 大小 + 新数量增量
else if (_Capacity < size() + _Count)
{
// 容量 = 容量 + 容量 / 2;
_Capacity = max_size() - _Capacity / 2 < _Capacity ? 0 : _Capacity + _Capacity / 2;
// 容量 < 大小 + 新增数量
if (_Capacity < size() + _Count)
// 容量 = 大小 + 新增数量
_Capacity = size() + _Count;
// 申请新内存
pointer _Newvec = this->_Alval.allocate(_Capacity);
// 开始引用地址
pointer _Ptr = _Newvec;
// 复制与删除过程
......
}
那么如何解决我即想引用vector的地址又不需要经常去刷新该地址呢?
同时又解决掉vector的复制问题,也解决掉有些类的复制构造?
方法还是有的。
这里就有前面提到的容量/大小的因素了。
容量必然大于大小,是肯定的,即:capacity >= size;
方法1:
创建std::vector容器,并调用std::vector::reserve(int n);
告诉vector的直接增加n个数据并分配内存,
这样的话push_back数量在n以内,引用的地址不会发生变化。vector内存也没有改变
但如果事先告诉n的程序,这种方法是最好的。
例:
std::vector< int > vA;
vA.reserve(10);
vA.push_back(7);
vA.push_back(6);
vA.push_back(2);
vA.push_back(9);
int* pAddress = &vA[0];
方法2:
通过构造函数告诉该容量容量
各种带参数构造类似,这里不多举例
例:
std::vector< int > vA(10);
int* pAddress = &vA[0];
pAddress[0] = 7; // 等于 vA[0] = 7;
pAddress[1] = 6; // 等于 vA[1] = 6;
pAddress[2] = 2; // 等于 vA[2] = 2;
pAddress[3] = 9; // 等于 vA[3] = 9;
方法3:
与方法1方法一样。只是调用函数是:std::vector::resize(int n);
再说说resize与reserve的区别
resize:新的大小 < 现有大小,会删除多余的部分
新的大小 > 现有大小,会在现有尺部插入新的数据,这时就有可能造成容量变化
其它情况不操作。
reserve:当前容量 < 传入容量,会根据新的容量创建内存,并复制。
当前容量 > 传入容量,不理踩
其它情况不操作。