memcpy实际上是一种值拷贝:在模拟实现的时候,拷贝构造和扩容如果都使用了mempy
所以在vector<T>模板T为内置类型的时候,不会出现问题
但如果是自定义类型的,特别是自定义类型中出现指针等,容易造成浅拷贝的问题下面我们来分析:
这是一种原始形式的拷贝函数的写法:当遇到内置类型的函数时候是一点毛病没有,但是如果遇到自定类型,就会出现下面的情况
加入我们将类型改成string,去拷贝构造
我们把v3拷贝给v4,如果用memcpy就会用第一层深拷贝实现了,但是第二层失败的情况:
重点分析:
v3和v4都有了自己独立的空间,此时第一层深拷贝完成;但是此时vector的元素类型是自定义类型string,string自己的_str还指向一篇动态开辟的空间,所以如果只是使用memcpy的话,就只能拷贝_str的值过去,但是新旧的_str指向的地址依旧是同一个,造成浅拷贝。
所以我们为了处理这种情况,选择使用赋值操作去完成自定义类型数据的拷贝(注意:使用赋值去完成拷贝,应该先保证此种自定义类型自身已经重载了赋值操作,如果仅仅依靠编译器默认生成的赋值函数,不足以完成深拷贝的目的)
代码展示:
vector(const vector<T>& v)
{
_start = new T[v.capacity()];
for (size_t i = 0; i < v.size(); i++)
{
_start[i] = v._start[i];
}
_finsh = _start + v.size();
_end_of_storage = _start + v.capacity();
}
运用赋值的时候,会自动调用自定义类型的赋值成员函数,即对成员完成深拷贝操作。
上面提到了,扩容操作(reserve)也是会导致出现第二层深拷贝失效,和上述是同样的道理,我们只要将memcpy改成赋值操作就可以了:
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();//提前把size记录下来
//不然后面开新空间后的_start就不是原来的_start了,会找不到原来的size()
T* tmp = new T[n];
//如果原本空间里值不为空,那么用memcpy拷贝过去
//memcpy(tmp, _start, sizeof(T) * size());
if (_start)
{
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
}
delete[] _start;
_start = tmp;
_finsh= _start + sz;
_end_of_storage = _start + n;
}
}