对于vector模拟实现的修正和补充

对于vector模拟实现的修正和补充

本文为万里原创,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;
}

来看看运行结果~~‍♀️

对于vector模拟实现的修正和补充_第1张图片

看运行结果,没有问题,正确输出四个字符串!✔

下面我们改一下代码,在尾插一个字符串(代码二)~~

//代码二:
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;
}

再看一下运行结果~~‍♂️

对于vector模拟实现的修正和补充_第2张图片

乍一看,貌似也没什么问题,正确输出五个字符串!

接下来我们进一步更改我们的代码(代码三)~~

//代码三:
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;
}

再来看一下代码的运行结果~~‍♂️

对于vector模拟实现的修正和补充_第3张图片

可以看到,程序运行出现了很奇怪的结果?!!!❌

经过调试分析,最后发现原来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等自定义类型,在拷贝时需要深拷贝!

对于vector模拟实现的修正和补充_第4张图片

所以我们要实现的是深拷贝!

代码实现如下~~‍♂️

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

对于vector模拟实现的修正和补充_第5张图片

对于vector模拟实现的修正和补充_第6张图片

我们发现,当我们存入的字符串较短时(长度小于16),字符串存在Buf这个数组中,当字符串较长时(长度大于16),从堆上申请空间,用Ptr指针指向堆上的那块空间.

也就是说:

当字符串长度小于16时,字符串存在string中,作为string中的一部分,在拷贝时也拷贝了一份,所以不会有问题;

而当字符串长度大于16时,字符串存在堆上,类中的Ptr指向对应堆上的空间,所以直接拷贝会出问题!

之后我又用g++测试了该代码,发现在Linux下,代码二三都会出问题~~

对于vector模拟实现的修正和补充_第7张图片

二.补充

在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;
		}
      //...
   }

感谢阅读~~

你可能感兴趣的:(C++,c++)