vector模拟实现
vector相关函数实现大纲
namespace yang
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
// construct and destroy
vector();
vector(int n, const T& value = T());
template<class InputIterator>
vector(InputIterator first, InputIterator last);
vector(const vector<T>& v);
vector<T>& operator= (vector<T> v);
~vector();
// capacity
size_t size() const;
size_t capacity() const;
void reserve(size_t n);
void resize(size_t n, const T& value = T());
//access
T& operator[](size_t pos);
const T& operator[](size_t pos)const;
//modify
void push_back(const T& x);
void pop_back();
void swap(vector<T>& v);
iterator insert(iterator pos, const T& x);
iterator erase(iterator pos);
private:
iterator _start; // 指向数据块的开始
iterator _finish; // 指向有效数据的尾
iterator _endOfStorage; // 指向存储容量的尾
};
}
construct and destroy
-
-
默认构造
构造一个空的容器,没有任何元素
vector() { _start = nullptr; _finish = nullptr; _endOfStorage = nullptr; }
-
n个元素构造
用n个元素构造一个容器,每个元素都是val
vector(int n, const T& value = T()) :_start(nullptr) ,_finish(nullptr) ,_endOfStorage(nullptr) { reserve(n); for (int i = 0; i < n; ++i) { push_back(value); } }
但是这个时候会遇到一个问题,就是如果我们调用vector
v1(10,1)时,这个时候运行就会报错"非法的间接寻址",会调用迭代器区间构造函数,这个是因为模版会调用更符合的。如果这个时候我们想用这个模版,就要想这样调用vector
v1(10u,1); -
迭代器区间构造
这里需要传一个模版,因为迭代器区间构造的区间可能是其他不确定容器的区间。
template<class InputIterator> vector(InputIterator first, InputIterator last) :_start(nullptr) , _finish(nullptr) , _endOfStorage(nullptr) { while (first != last) { push_back(*first); ++first; } }
-
拷贝构造函数
1. 传统写法
即开辟一块与容器相同的空间,然后将容器中的值一个一个拷贝过来.vector(const vector<T>& v) :_start(nullptr) , _finish(nullptr) , _endOfStorage(nullptr) { _start = new T[v.capacity]; for (int i = 0; i < v.size(); ++i) { _start[i] = v[i]; } _finish = _start + v.size(); _endOfStorage = _start + v.capacity(); }
- 现代写法
使用范围for(或是其他遍历方式)对容器v进行遍历,在遍历过程中将容器v中存储的数据一个个尾插过来即可。
这里的遍历的e就是v的每一个数据的拷贝,相当于调用v类型的拷贝构造(深拷贝).vector(const vector<T>& v) :_start(nullptr) , _finish(nullptr) , _endOfStorage(nullptr) { reserve(v.capacity()); for (auto e : v) { push_back(e); } }
-
赋值运算符重载函数
- 传统写法
:首先判断是否是给自己赋值,若是给自己赋值则无需进行操作。若不是给自己赋值,则先开辟一块和容器v大小相同的空间,然后将容器v当中的数据一个个拷贝过来,最后更新_finish和_endofstorage的值即可。
vector<T>& operator= (vector<T>& v) { if (this != &v) { delete _start; _start = new T[v.capacity()]; for (int i = 0; i < v.size(); ++i) { _start[i] = v[i]; } _finish = _start + v.size(); _endOfStorage = _start + v.capacity(); } return *this; }
- 现代写法
:会直接调用参数的 拷贝构造(传值传参要调用拷贝构造)
vector<T>& operator= (vector<T> v) { swap(v); return *this; }
-
析构函数
~vector() { //这里记得配套使用 delete[] _start; _start = nullptr; _finish = nullptr; _endOfStorage = nullptr; }
迭代器相关函数
> // Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
普通迭代器和const迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator cbegin() const
{
return _start;
}
const_iterator cend() const
{
return _finish;
}
加入cosnt迭代器的目的是为了保证const对象调用该函数时只能进行对数据读操作.
capcity相关函数
size和capacity
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endOfStorage - _start;
}
resize和reserve
- reserve
void reserve(size_t n)
{
if (n > capacity())
{
int sz = size();//有效个数
T* tmp = new T[n];
if (_start)
{
for (int i = 0; i < sz; ++i) {
tmp[i] = _start[i];
}
}
delete[] _start;//释放该旧空间
_start = tmp;
_finish = _start + sz;
_endOfStorage = _start + n;
}
}
假设模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝,一下代码会发生什么问题呢?
- 1.memcpy是内存的二进制格式拷贝,将一段内存空间中内存原封不动的拷贝到另外一段内存空间中。
2.如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy时浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。
2. resize
void resize(size_t n, const T& value = T())
{
if (n < size())
{
//缩小
_finish = _start + n;
}
else//当n大于当前的size时
{
if (n > capacity()) reserve(n);
while (_finish != _start + n)
{
*_finish = value;
_finish++;
}
}
}
modify
-
push_back
//需要先判断是否要扩容 void push_back(const T& x) { if (_finish == _endOfStorage) { size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; } *_finish = x; _finish++; }
-
pop_back
//最好断言一下,以至于来看看还有没有有效元素 bool empty()const { return _start == _finish; } void pop_back() { assert(!empty()); _finish--; }
-
insert
//判断是否需要扩容,在此扩容之前记录一下pos和start的距离,然后扩容完之后移动元素,最后再插入元素。
iterator insert(iterator pos, const T& x)
{
if (_finish == capacity())
{
size_t len = pos - _start;
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
pos = _start + len;
}
size_t end = _finish;
while (end >= pos)
{
*end = *(end - 1);
--end;
}
//插入
*pos = x;
_finish++;
return pos;
}
- erase
//先判断是否为空,然后从前往后移动数据,最后_finish减减
iterator erase(iterator pos)
{
assert(!empty());
size_t end = pos + 1;
while (end != _finish)
{
*(end - 1) = *(end);
++end;
}
_finish--;
return pos;
}
5.erase
//用库函数里面的swap
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endOfStorage, v._endOfStorage);
}
accesee
- operator[]和const opeartor[]
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}