作者前言
🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂
vector的模拟
vector的模拟实现
STL源码
我们可以查看里面的vector的源码,
我们可以发现有三个
iterator start;
iterator finish;
iterator end_of_storage;
大概意思就是
下面我们来简单模拟一些常用的成员函数,其中需要注意的就是扩容的话,我这里采用的是两倍扩容,
namespace bit
{
template <typename T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
{
_start = nullptr;
_finish = nullptr;
_end_of_storage = nullptr;
}
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
if(_start)
memcpy(tmp, _start, sizeof(T) * n);
_finish = tmp + size();
delete[] _start;
_start = tmp;
_end_of_storage = _start + n;
}
}
void push_back(const T& a)
{
if (_finish == _end_of_storage)
{
//扩容
size_t newcapacity = size() > 0 ? 2 * size() : 4;
reserve(newcapacity);
}
*_finish = a;
_finish++;
}
size_t size()
{
return _finish - _start;
}
size_t capacity()
{
return _end_of_storage - _start;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
}
这些成员函数是比教常用的,这里只是简单的模拟一下,有兴趣的话可以去看看STL源码
需要注意的模拟的reserve是这样的写法只适用于一些内置类型如, int 、char…,,
如果是自定义类型的话,就拿
vector<string> 来说,
在reserve中使用memcpy来进行,会导致拷贝的string(),指向的空间是一个野指针,我们可以构造一个析构函数,采用引用计数法来决定这块空间是否释放,或者我们自己写一个赋值的方法来 解决
赋值的方法:
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
if (_start)
//memcpy(tmp, _start, sizeof(T) * n);
{
for (int i = 0; i < size(); i++)
{
tmp[i] = (*this)[i];
}
}
_finish = tmp + size();
delete[] _start;
_start = tmp;
_end_of_storage = _start + n;
}
}
insert模拟
void insert(size_t pos, const T& content)
{
assert(pos <= size());
if (_finish == _end_of_storage)
{
//扩容
size_t newcapacity = size() > 0 ? 2 * size() : 4;
reserve(newcapacity);
}
//后移
bit::vector<T>::iterator then = _finish;
while (_finish >= _start + pos)
{
*(_finish + 1) = *(_finish);
_finish--;
}
_finish = then + 1;
*(_start + pos) = content;
}
void insert(iterator pos, const T& content)
{
assert(pos>=_start && pos <= _finish);
if (_finish == _end_of_storage)
{
//扩容
size_t newcapacity = size() > 0 ? 2 * size() : 4;
reserve(newcapacity);
}
size_t postion = (pos - _start) / sizeof(T);//记录pos的相对位置,因为pos位置可能被释放了
//后移
bit::vector<T>::iterator then = _finish;
while (_finish >= _start + postion)
{
*(_finish + 1) = *(_finish);
_finish--;
}
_finish = then + 1;
*(_start + postion) = content;
}
这里我模拟了两个,一个是通过下标的,一个是通过迭代器的
需要注意的是迭代器这个,
迭代器这个在开辟新的地址的时候,会把原来的空间释放掉,pos也就成为了野指针,因此,我们只需进行计算出原来的pos的相对位置,然后在新的地址上找出相对应的位置就行,
erase模拟
void erase(iterator postion)
{
assert(postion >= _start && postion < _finish);
while (postion+1 < _finish)
{
*postion = *(postion + 1);
postion++;
}
_finish = postion;
}
void erase(iterator first,iterator last)
{
assert(first >= _start && last <= _finish);
iterator postion1 = first;
iterator postion2 = last;
while (postion2 < _finish)
{
*postion1 = *(postion2);
postion1++;
postion2++;
}
_finish = postion1;
}
这里简单的模拟一下,
迭代器失效
在上面的模拟的insert的函数中就有这个情况,
使用reserve是有可能使_start改变的,跟c语言的realloc是一样的,开辟一块新的空间,我这里模拟的pos是一个下标,不是迭代器,如果pos是一个迭代器,那么就有可能指向的地址早已经被释放掉了,改变这块地址的内容,没有意义,所以我们只能找到对应位置来修改,这是一种解决内部的迭代器失效,
外部的迭代器失效还是会存在, 一般有这种情况,我们不会使用外面的迭代器,因为那是没有意义的.
还要一些情况
在vs中使用erase删除,it就会失效的,这个由编译器导致的,不同的编译器有不同的,还有就是earse本身删除数据会不会缩容,也是编译器决定的,如果缩容了,_start也会有可能改变,it也就会失效
总结: 迭代器失效一般情况是野指针,或者指向的那块地址不属于原来的那块开辟的空间,