本文为万里原创,CSDN首发
发布时间:2022/3/26
欢迎大家点赞❤收藏✨加关注
✒本文大约2400词左右
笔者水平有限,如有错误,还望告诉笔者,万分感谢!
有什么问题也可在评论区一起交流哦!
之前发了一篇vector模拟实现的博文,我以为没什么问题,结果自己最近在复习代码时遇到了一些问题!(下面链接是之前的博文)
vector的模拟实现让我们来看看出了那些问题~~
对于下面这一段代码(代码一),是构造了一个元素是string的vector,然后尾插4个字符串,最后输出每个字符串
//代码一:
int main()
{
V::vector<string> v;
v.push_back("111111");
v.push_back("111111");
v.push_back("111111");
v.push_back("111111");
for (auto& e : v)
{
cout << e << endl;
}
return 0;
}
来看看运行结果~~♀️
看运行结果,没有问题,正确输出四个字符串!✔
下面我们改一下代码,在尾插一个字符串(代码二)~~
//代码二:
int main()
{
V::vector<string> v;
v.push_back("111111");
v.push_back("111111");
v.push_back("111111");
v.push_back("111111");
v.push_back("111111");
for (auto& e : v)
{
cout << e << endl;
}
return 0;
}
再看一下运行结果~~♂️
乍一看,貌似也没什么问题,正确输出五个字符串!
接下来我们进一步更改我们的代码(代码三)~~
//代码三:
int main()
{
V::vector<string> v;
v.push_back("1111111111111111111111111111111");
v.push_back("1111111111111111111111111111111");
v.push_back("1111111111111111111111111111111");
v.push_back("1111111111111111111111111111111");
v.push_back("1111111111111111111111111111111");
for (auto& e : v)
{
cout << e << endl;
}
return 0;
}
再来看一下代码的运行结果~~♂️
可以看到,程序运行出现了很奇怪的结果?!!!❌
经过调试分析,最后发现原来bug在增容reserve中!
void reserve(size_t n)
{
if (n > capacity())
{
//保存一份size
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T)*sz);
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_Storage = _start + n;
}
}
对于该reserve,在大多数情况下是没问题的,但这里怎么出问题了呢?
原因是但我们需要增容时,是开了一块新空间,然后将就空间的内容拷贝到新空间上,而这里用到了memcpy ,memcpy是字节序拷贝,也就是浅拷贝!
对于内置类型,memcpy用着没有问题,但是对于string等自定义类型,在拷贝时需要深拷贝!
所以我们要实现的是深拷贝!
代码实现如下~~♂️
void reserve(size_t n)
{
if (n > capacity())
{
//保存一份size
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T)*sz);
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];//若是自定义类型,会调用其深拷贝构造函数
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_Storage = _start + n;
}
}
认真读到这里的读者一定在心中有一个黑人问号?
**为什么代码二没有崩?**
它不也是浅拷贝,按理来说在调用析构函数时应该会出问题鸭?
要解释清楚这个问题,就要介绍一下VS中对string做的处理~~
打开调试窗口,我们发现string内部_Bx下又两个东西:
一个是大小为16的Buf数组,一个类型为char*的指针Ptr
我们发现,当我们存入的字符串较短时(长度小于16),字符串存在Buf这个数组中,当字符串较长时(长度大于16),从堆上申请空间,用Ptr指针指向堆上的那块空间.
也就是说:
当字符串长度小于16时,字符串存在string中,作为string中的一部分,在拷贝时也拷贝了一份,所以不会有问题;
而当字符串长度大于16时,字符串存在堆上,类中的Ptr指向对应堆上的空间,所以直接拷贝会出问题!
之后我又用g++测试了该代码,发现在Linux下,代码二三都会出问题~~
在vector的模拟实现中,我们漏了对迭代器的模拟实现,我们在这里把他补上~~
template<class T>
class vector
{
typedef T* iterator;//可读可写
typedef const T* const_iterator;//只读
public:
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
//...
}
感谢阅读~~