C++之STL容器-----Vector实现
一、Vector容器底层实现简易设计
(Vector内存图纵览)
(实现代码总览)
namespace wanboss
{
template<class T>
class vector
{
public:
typedef T* itetator;
//构造函数
vector()
:_start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)//初始化成员变量。
{ }
//拷贝构造函数 vector v(v1)
vector(const vector<T>& v)//this-->v, const vector<T>& v-->v1。
{
_start = new T[v.capacity()];//创建一块以v1为基准的内存。
_finish = _start;//新内存中没有数据,因此将尾指针指向开始位置。
_end_of_storage = _start + v.capacity();//相当于capacity().
for (size_t i = 0; i < v.size(); ++i)
{
//将 v 中值逐个拷贝给新的空间中,v[i] 会调用 operator[]方法。
*_finish = v[i];
++_finish;//元素进来得移动尾指针。
}
}
//析构函数
~vector()
{
delete[] _start;//释放内存空间
_start = _finish = _end_of_storage = nullptr;//将指针置为空。
}
//迭代器的开始位置
itetator begin()
{
return _start;
}
//迭代器的尾巴位置
itetator end()
{
return _finish;
}
//const-->非const可以访问,const也可访问。
size_t size() const
{
return _finish - _start;
}
//const-->非const可以访问,const也可访问。
size_t capacity()const
{
return _end_of_storage - _start;
}
//operator[] 重定义。
T& operator[](size_t i)
{
return *(_start + i);
}
//const-->非const可以访问,const也可访问。
const T& operator[](size_t i)const
{
return *(_start + i);
}
//------------------------传统写法---------------------------------
//v1(v2)
vector<T>& operator=(const vector<T>& v)
{
if (this != &v)//防止 s1 = s1 防止降低效率。
{
delete[] _start;//释放原来的内存空间
//基于 v2 创建一块新空间,并让原来旧空间指针指向。
_start = new T[v.capacity()];
//memcpy,按字节拷贝,将 v2 的内容拷贝给 v1。
memcpy(_start, v._start, sizeof(T) * v.size());
}
return *this;
}
//------------------------传统写法---------------------------------
//------------------------现代写法---------------------------------
vector<T>& operator=(const vector<T>& v)
{
swap(_start, v._start);
swap(_finish, v._finish);
swap(_end_of_storage, v._end_of_storage);
return *this;
}
//------------------------现代写法---------------------------------
//reserve() 是改变容量的
void reserve(size_t n)// n-->就是新的容量。
{
if (n > capacity())//如果 新空间大于总容量,增容。
{
//在进行内存操作之前,保存当前的大小,防止丢失。
size_t old_size = size();
T* temp = new T[n];//开辟一块临时空间
/*
如果 _start 为 nullptr,则表示当前 vector 是空的,没有任何已分配的内存,
则无需进行以下的内存复制和释放操作,因为没有数据需要被复制,也没有内存需要被释放。
*/
if (_start)//
{
//memcpy(temp, _start, sizeof(T)* old_size);//按字节进行拷贝,属于浅拷贝
for (size_t i = 0; i < old_size; ++i)
temp[i] = _start[i];//调用的是 T 的operator=,属于深拷贝。
delete[] _start;//释放原来的空间。
}
_start = temp;//将旧的指向变成新的空间指向。
_finish = temp + old_size;//新的空间指向 + 旧的数据大小 = 新的空间数据结尾
_end_of_storage = temp + n;//新的空间指向 + 旧的空间容量 = 新的空间容量。
}
}
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
itetator it = _start;
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
void push_back(const T& x)
{
//if (_finish == _end_of_storage)//首先判断,容量是否够?
//{
// size_t newcapacoty = capacity() == 0 ? 2 : capacity() * 2;//不够,增容。
// reserve(newcapacoty);
//}
//*_finish = x;//尾插,将_finish指向的空间填入尾插的值
//++_finish;//更新尾指针。
insert(_finish, x);
}
void pop_back()
{
//if (_finish >= _start)
// _finish--;
//assert(_finish >= _start);//加入一个断言比较符合规定。
erase(_finish - 1);
}
void insert(itetator pos, const T& x)//表示插入一个元素
{
assert(pos <= _finish);//表示可以尾插
if (_finish == _end_of_storage)
{
size_t len = pos - _start;//记录pos在增容前,与头指针距离。
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
pos = _start + len;//更新pos在增容后,新空间指向问题。
}
itetator end = _finish - 1;//尾部第一个数。
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
itetator erase(itetator pos)
{
if (_end_of_storage != _start)
{
assert(pos < _finish&& pos >= _start);
itetator newstart = pos;
while (newstart < _finish)
{
*newstart = *(newstart + 1);
++newstart;
}
--_finish;
return pos;
}
else
{
return nullptr;
}
}
private:
itetator _start;
itetator _finish;
itetator _end_of_storage;
};
}
二、Verctor类实现细节
2.1 构造函数
、拷贝构造
和析构函数
//---------------------构造函数-----------------------------------
/*一般的构造函数对成员变量进行初始化工作,这里的成员变量是指针,一般让其指向空*/
vector()
:_start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)//初始化成员变量。
{ }
//---------------------构造函数-----------------------------------
//---------------------拷贝构造函数-----------------------------------
//拷贝构造函数 vector v2(v1)
/*碰到拷贝构造函数,就可以想到 Vector v2(v1) 这个示例,主要是分清楚:
v1 和 v2 在拷贝构造中代表这谁。技巧:谁基于谁才创建的----> v2 是基于 v1 创建的*/
vector(const vector<T>& v)//this-->v2, const vector<T>& v-->v1。
{
_start = new T[v.capacity()];//创建一块以v1为基准的内存。
//this->_start = new T[v.capacity()];
_finish = _start;//新内存中没有数据,因此将尾指针指向开始位置。
_end_of_storage = _start + v.capacity();//相当于capacity().
for (size_t i = 0; i < v.size(); ++i)
{
//将 v 中值逐个拷贝给新的空间中,v[i] 会调用 operator[]方法。
*_finish = v[i];
++_finish;//元素进来得移动尾指针。
}
}
//---------------------拷贝构造函数-----------------------------------
//析构函数
~vector()
{
delete[] _start;//释放内存空间
_start = _finish = _end_of_storage = nullptr;//将指针置为空。
}
针对拷贝构造函数,绘制思维图理解。
2.2 迭代器相关—begin( )、end( )、size( )、capacity( )。
( 迭代器 浅层可以认为是 一个指针
)
//迭代器的开始位置
itetator begin()
{
return _start;
}
//迭代器的尾巴位置
itetator end()
{
return _finish;
}
//const-->非const可以访问,const也可访问。
size_t size() const
{
return _finish - _start;
}
//const-->非const可以访问,const也可访问。
size_t capacity()const
{
return _end_of_storage - _start;
}
从下图可以看出上述代码的关系图:
2.3 有关 operator 的运算符重载:operator = 和 operator [ ]
//operator[] 重定义。
T& operator[](size_t i)
{
return *(_start + i);
}
//const-->非const可以访问,const也可访问。
const T& operator[](size_t i)const
{
return *(_start + i);
}
//------------------------传统写法---------------------------------
//v1 = v2
vector<T>& operator=(const vector<T>& v)
{
if (this != &v)//防止 s1 = s1 防止降低效率。
{
delete[] _start;//释放原来的内存空间
//基于 v2 创建一块新空间,并让原来旧空间指针指向。
_start = new T[v.capacity()];
//memcpy,按字节拷贝,将 v2 的内容拷贝给 v1。
memcpy(_start, v._start, sizeof(T) * v.size());
}
return *this;
}
//------------------------传统写法---------------------------------
//------------------------现代写法---------------------------------
vector<T>& operator=(const vector<T>& v)
{
swap(_start, v._start);
swap(_finish, v._finish);
swap(_end_of_storage, v._end_of_storage);
return *this;
}
//------------------------现代写法---------------------------------
有关于const问题:(可参考这篇博客–关于const修饰成员变量)
关于运算符重载问题
2.4 reserve( )
和 resize( )
函数
reserve() : 改变容量(capacity())
、resize() :改变有效字符大小个数(size())
//reserve() 是改变容量的
void reserve(size_t n)// n-->就是新的容量。
{
if (n > capacity())//如果 新空间大于总容量,增容。
{
//在进行内存操作之前,保存当前的大小,防止丢失。
size_t old_size = size();
T* temp = new T[n];//开辟一块临时空间
/*
如果 _start 为 nullptr,则表示当前 vector 是空的,没有任何已分配的内存,
则无需进行以下的内存复制和释放操作,因为没有数据需要被复制,也没有内存需要被释放。
*/
if (_start)//
{
//memcpy(temp, _start, sizeof(T)* old_size);//按字节进行拷贝,属于浅拷贝
for (size_t i = 0; i < old_size; ++i)
temp[i] = _start[i];//调用的是 T 的operator=,属于深拷贝。
delete[] _start;//释放原来的空间。
}
_start = temp;//将旧的指向变成新的空间指向。
_finish = temp + old_size;//新的空间指向 + 旧的数据大小 = 新的空间数据结尾
_end_of_storage = temp + n;//新的空间指向 + 旧的空间容量 = 新的空间容量。
}
}
void resize(size_t n, const T& val = T())//改变有效内容的大小,并将新的内容进行初始化。
{
if (n < size())//
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
itetator it = _start;
//这一步比较关键,因为resize他是改变有效内容的大小并对新的空间进行初始化。
//因此是在_finish之后添加,可以保证 n < size()时,不会改变原有内容。
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
关于reserve( )的设计思绪图:
面试点:为什么reserve在进行拷贝的时候要使用代码上的形式,使用memcpy函数
不行?