C++vector类的常见函数以及其模拟实现
文章目录
前言
C++中引入了stl标准模板库,其中就包括了vector。
vector的核心就是数据结构中的顺序表。不过这个顺序表是动态开辟的,可随着用户的不断插入而增大容量。当然,每次增大容量都必须去耗费时间去copy数据,所以vector中通常会预留一些空间来提高效率。接下来我们就来介绍一下vector的常见函数以及它的模拟实现。
一、vector的内部成员变量有什么?
首先定义了一个模板T,这样可以在vector中插入不同的数据类型,当然也包括自定义数据类型。
然后将T*重命名为iterator(增加可读性)。
_start 指向vector的第一个元素的位置。
_finish 指向vector最后一个元素的下一个位置。
_end_of_storage 指向_start+容量的位置。(表示容量)
template<class T>
class vector
{
typedef T* iterator;
typedf const T* const_iterator;
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
二、vector的构造\析构函数以及=的重载
1.默认构造
vector()
:_start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{}
2.迭代器构造
这里定义了一个迭代器模板InputIterator,使得不管传入什么样的迭代器都可以进行构造。
template<class InputIterator>
vector(InputIterator start, InputIterator end)
:_start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{
while (start != end)
{
push_back(*start); // push_back即在末尾插入元素
start++;
}
}
3.传参构造
之所以又定义了一个int类型的重载,是因为当传入参数为int&&模板类型T=int时,这时编译器会默认去调用迭代器构造,从而出错。
vector(size_t n, const T& value = T())
:_start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; i++)
{
_start[i] = value;
}
_finish = _start + n;
_end_of_storage = _finish;
}
vector(int n, const T& value = T())
:_start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{
reserve(n);
for (int i = 0; i < n; i++)
{
_start[i] = value;
}
_finish = _start + n;
_end_of_storage = _finish;
}
4.拷贝构造以及重载
这里的cbegin,cend会传回const_iterator
swap 会将两个vector的成员依次交换(后面我们会提到)
vector(const vector<T>& vv)
:_start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{
reserve(vv.capacity());
const_iterator it = vv.cbegin();
iterator me = begin();
while (it != vv.cend())
{
*(me++) = *(it++);
}
_finish = _start + vv.size();
_end_of_storage = _finish;
}
vector<T> operator=(vector<T>& v)
{
vector<T> temp(v);
swap(temp);
return *this;
}
5.析构函数
~vector()
{
delete[] _start;
_start = nullptr;
_end_of_storage = nullptr;
_finish = nullptr;
}
三.常见函数
1.reserve函数
传入一个变量n,若n大于其容量,则去扩容。
扩容后将原元素移过来,并且释放原空间,并更新_start,_finish,_end_of_storage。
void reserve(size_t n)
{
if (n > capacity())
{
int old_size = size();
T* temp = new T[n];
if (_start != nullptr)
{
for (size_t i = 0; i < old_size; i++)
{
temp[i] = _start[i];
}
delete[] _start;
}
_start = temp;
_finish = _start + old_size;
_end_of_storage = _start + n;
}
else
return;
}
2.insert函数
传入一个迭代器pos,以及一个值value。即在pos位置插入val。
当容量不够用时,即(_finish == _end_of_storage),此时需要去扩容(这里如果不为空,我们按2倍扩容)。扩容完需要更新pos的值,否则会出现迭代器失效问题。
然后从末尾依次移动元素,最后将val插入,更新_finish。
注意:最后需要将pos的值作为函数传参传回去。因为如果插入过程中有扩容行为的话,原pos就会失效。
iterator insert(iterator pos, const T& value)
{
assert(pos<=_finish);//pos必须在有效范围内插入
if (_finish == _end_of_storage)
{
size_t len = pos - _start;
size_t new_capacity =
(capacity() == 0)?1 : capacity() * 2;
reserve(new_capacity);
pos = _start + len; // 扩容后更新
}
for (iterator p = _finish; p > pos; p--)
{
*p = *(p - 1);
}
*pos = value;
++_finish;
return pos;
}
3.erase函数
传入一个迭代器,表示删除pos位置的元素。
依次将pos后面的位置移动上来,最后更新_finish。
注意:这里需要将原pos位置传回去。如果不传,也会出现迭代器失效问题。
iterator erase(iterator pos) //删除pos位置的元素
{
assert(pos < end()); pos也必须在有效位内
iterator p = pos;
while (p < end()-1)
{
*p = *(p + 1);
p++;
}
_finish--;
return pos;
}
4.resize函数
传入一个n
如果n小于它的size,则将原size变为n即可。
如果n大于它的size,则需扩容。并将后面的值更新为value。
void resize(size_t n,const T& value=T())
{
if (n < size())
{
_finish = _start + n;
return;
}
else
{
reserve(n);
for (size_t i = size(); i < n; i++)
{
_start[i] = value;
}
_finish = _start + n;
return;
}
}
5.push_back函数
直接调用insert即可
void push_back(const T& value)
{
insert(_finish, value);
}
6.pop_back函数
直接调用erase函数即可
void pop_back()
{
erase(end()-1);
}
7.swap函数
调用库里的swap依次交换即可
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
8.capacity,size,empty,front,back函数
size_t capacity()const
{
return _end_of_storage - _start;
}
size_t size()const
{
return _finish - _start;
}
bool empty()
{
if (size()==0||_start == nullptr)
return true;
else
return false;
}
T& front()
{
assert(_start != nullptr);
return *_start;
}
T& back()
{
assert(_finish != nullptr);
return *(_finish-1);
}
四.迭代器
1.正向迭代器
因为这里iterator本质为char* 自己就可自增自减,解引用。所以无需封装
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator cbegin()const
{
return _start;
}
const_iterator cend()const
{
return _finish;
}
T& operator[](size_t n)
{
assert(n < size());
return _start[n];
}
2.反向迭代器
rbegin()返回end()
rend()返回begin()
注意每次解引用应为后一个位置
template<class T>
class vector
{
public:
class ReverseIterator{
public:
typedef iterator reverse_iterator;
typedef ReverseIterator self;
ReverseIterator(iterator it)
:_it(it)
{}
self& operator++()
{
return ReverseIterator(--_it);
}
T& operator*()
{
return *(_it - 1);
}
self& operator--()
{
return ReverseIterator(++_it);
}
bool operator!=(self& it)
{
return _it != it._it;
}
private:
reverse_iterator _it;
};
public:
typedef ReverseIterator reverse_iterator;
typedef const ReverseIterator const_reverse_iterator;
reverse_iterator rbegin()
{
return end();
}
reverse_iterator rend()
{
return begin();
}
};
总结
以上就是vector的常用函数以及模拟实现(本人小白一枚,若有错误还望各位指正)
完整的代码存放在gitee上:https://gitee.com/jj-wen/c-beginner/blob/master/06.20%EF%BC%88%E5%A4%8D%E4%B9%A0vector%EF%BC%89/06.20%EF%BC%88%E5%A4%8D%E4%B9%A0vector%EF%BC%89/vector.h
学习任重而道远,希望自己可以坚持下去。