c语言中的顺序表也就是cpp中的vector,但vector用指针的方式存储数据,同时vector中可以同时存储多个自定义类型例如:vector<string>或者vector<vector<int>>来书写对象,现在我将带你了解基本的底层实现。
vector的框架
namespace xiaobo
{
template<class T>//模板(让任何类型都能调用vector)
class vector
{
typedef T* iterator;//相当于迭代器的名
public:
//返回size
size_t size()
{
return _finish-_start;
}
//返回capacity
size_t capacity()
{
return _endofstorage-_start;
}
//迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
private:
iterator _start;//开始
iterator _finish;//_start+size()=_finish存储数据的指针
iterator _endofstorage;//有多少空间相当于capacity()
};
}
构造函数vector()
vector()
//在定义的过程中让所有的成员为空
:_start(nullptr),//
_finish(nullptr),
_endofstorage(nullptr)
{}
析构函数~vector()
~vector()
{
delete[]_start;//释放空间
//全部置为空
_start = _finish = _endofstorage = nullptr;
}
拷贝函数vector()[old]
//老方法,过程复杂但是思路简单
vector(const vector<T>& tmp)
{
_start = new T[tmp.capacity()];//深拷贝需要创建新的内存
_finish = _start;
_endofstorage = _start + tmp.capacity();
//_finish来拷贝数据,后面会说明为什么不使用memset拷贝
size_t i = 0;
while (_finish < tmp._finish)
{
*_finish = tmp[i];
++_finish;
}
}
拷贝函数vector()[new]
//新方法代码简介且效率更高
vector(const vector<T>& tmp)
:_start(nullptr),
_finish(nullptr),
_endofstorage(nullptr)
{
reserve(tmp.capacity());
//创建于tmp空间大小相同的内存,来减少push_back的扩容时间(提高效率)
//用迭代器将每一个数据push_back()其中
for (auto e : tmp)
push_back(e);
}
新方法需要reserve()和push_back()两个函数,因此接下来将了解两个函数的底层原理
reserve()
void reserve(size_t n)
{
//判断扩容后的容量是否比capacity()大,需要扩容吗
if (n > capacity())
{
//这里的sz十分重要,由于后期的释放_start后依然需要使用size()
//但是_start释放后计算_finish-_start会导致内存错误
size_t sz = size();
iterator tmp = new T[n]();//开辟新的内存且初始化
//当_start==nullptr说明第一次增加数据并没有数据需要拷贝
if (_start)
{
//这里不使用memset函数拷贝原因会在最后解答
int n = 0;
while (tmp + n < _finish)
{
*(tmp + n) = *(_start + n);
++n;
}
//释放old内存,防止内存泄漏
delete[] _start;
}
_start = tmp;//深拷贝完成
_finish = tmp+sz;
_endofstorage = tmp+n;
}
}
push_back()
void push_back(T x)
{
if (_finish == _endofstorage)//发现容量不足时,进行扩容
{
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
//如果是第一次扩容就直接等于2否则扩大2倍
reserve(newcapacity);
}
//添加数据,增加size()
*_finish = x;
++_finish;
}
insert()
当提起push_back时就不得不提起insert(),因为插入函数,插入位置为最后一个时就变成了push_back()函数,接下来就是实现push_back函数的哥哥insert()
void insert(iterator pos, T& x)
{
//判断是否越界
assert(pos <= _finish&&pos>=_start);
if (_finish == _endofstorage)//扩容
{
size_t n = pos - _start;
//由于扩容会导致上一空间释放,_start和pos等等指针都会生效
//导致无法知道pos的具体位置只能先记录下数值后面再加回来即可
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
//由于扩容导致之前的pos指针失效使得用指针加数值的方式
pos = _start + n;
}
//拷贝数值
iterator end = _finish-1;
while (pos <= end)
{
*(end + 1) = *end;
--end;
}
//在pos位置添加数值,增加size()
*pos = x;
++_finish;
}
erase()
有增加必然会有删除erase()函数
iterator& erase(iterator pos)
{
//防止超出范围
assert(pos < _finish&&pos>=_start);
//将pos后面位置数值往前挪即可
iterator it = pos;
while (it != _finish)
{
*it = *(it + 1);
++it;
}
--_finish;
return pos;
}
符号重载
operator[]
//cout<<a[0]<<endl;表明a[0]是什么
//用于vector<int> a;
//cout<<a[0]<<endl;表明a[0]是什么
iterator& operator[](size_t i)
{
return _start+i;
}
operator=[old]
//a=b;用vector的赋值符号重载[old]
//old方法
vector<T> operator=(const vector<T>& v)
{
if (_start != v._start)
{
delete[]_start;//释放就空间
_start = new T[v.capacity()];//开辟心空间
//拷贝数据
for (size_t i = 0; i < v.size(); ++i)
{
*(_start + i) = *(v._start+i);
}
_finish = v._finish;
_endofstorage = v._endofstorage;
}
return *this;
}
operator=[new]
//利用拷贝函数拷贝出一个新空间的vector<T> v
vector<T> operator=(vector<T> v)
{
if (_start != v._start)
{
Swap(v);//再将里面全部交换即可,而且v在函数结束时会自动释放空间
}
}
void Swap(const vector& v)
{
//用库中的swap来交换内置类型,即可
::swap(v._start, _start);
::swap(v._finish, _finish);
::swap(v._endofstorage, _endofstorage);
}
resize()
最后一个复杂的函数resize()
void resize(size_t n, const T& val = T())
{
//n小于size()时需要丢弃n以后的数据
if (n < size())
{
*(_start + n) = val;
_finish =_endofstorage= _start+n;
}
else
{
if ( n > capacity())//内存不足时扩容
{
reserve(n);
}
//让未填写数据的空间写上val
while(_start+n<=_endofstorage)
{
*(_start + n) = val;
++n;
}
//表明已经写满
_finish = _endofstorage;
}
}
注意:memset()
由于memsert()是按字节处理也就是说:
int main()
{
int arr[10];
memset(arr, 0, sizeof(int) * 10);
for (auto e : arr)
{
cout << e << ' ' ;
}
cout << endl;
memset(arr, 1, sizeof(int) * 10);
//memset只按字节进行修改,也就是
//00000001 00000001 00000001 00000001
for (auto e : arr)
{
cout << e << ' ';
}
cout << endl;
memset(arr, 2, sizeof(int) * 10);
//为一个int的值,所以memset只适合char使用
//同理00000002 00000002 00000002 00000002
for (auto e : arr)
{
cout << e << ' ';
}
cout << endl;
return 0;
}
输出结果为
因此不能使用memset