选择memcpy拷贝:
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldSize = size(); //oldSize:原vector容器的大小
// 1. 开辟新空间
T* tmp = new T[n];
// 2. 拷贝元素
if (_start)
{
memcpy(tmp, _start, sizeof(T)*size);
// 3.释放旧空间
delete[] _start;
} //_start:vector容器的首地址
_start = tmp;
_finish = _start + oldSize; //_finsh:vector容器的最后一个元素的地址
_endOfStorage = _start + n; //_endOfStorage:vector容器的尾地址
}
}
结果:报错,程序崩溃
问题分析:
1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。(重点)
2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
memcpy拷贝的过程分析:
实现reserve接口时,采用的是异地扩容:
// 1. 开辟新空间
T* tmp = new T[n];
异地扩容的特点就是从去其他地方找一块更大的连续空间然后将目标数据拷贝过去再把原来的空间释放掉;如果使用memcpy进行拷贝,将这一段内存空间中内容原封不动的拷贝到异地内存空间中,就相当于破坏了新扩容空间的连续性,会导致空间丢失。
例:
int main()
{
bite::vector<bite::string> v;
v.push_back("1111");
v.push_back("2222");
v.push_back("3333");
return 0;
}
使用memcpy拷贝
memcpy拷贝后,新开辟的空间被覆盖了
释放旧空间后,相当于“新”空间也被释放了
结论:
如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。
实现reserve接口建议方案:
1.赋值拷贝:
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldSize = size(); //oldSize:原vector容器的大小
// 1. 开辟新空间
T* tmp = new T[n];
// 2. 拷贝元素
if (_start)
{
for (size_t i = 0; i < oldSize; ++i) //赋值拷贝
tmp[i] = _start[i];
// 3. 释放旧空间
delete[] _start;
}
// 3.释放旧空间
delete[] _start;
} //_start:vector容器的首地址
_start = tmp;
_finish = _start + oldSize; //_finsh:vector容器的最后一个元素的地址
_endOfStorage = _start + n; //_endOfStorage:vector容器的尾地址
}
}