在学习过程种 ,学习看源码是很有必要的,它可以让你更清楚的知道一些代码的细节,你也会发现源码将效率利用到了极致,不得不佩服写出源码的人。
下面我将配合源码来实现一个简单的vector,下面请看源码
看源码我们首先要看的就是成员变量,我们可以看到源码中有迭代器的start、finish、end_of_storage三个成员变量,然后可以看到它将一些类型进行重命名。我们可以将它理解为下面的这张图。
其次我们接下来看它的构造函数,它提供了无参和带参的构造函数,看到有fill_initialize这个函数,不难猜一猜可能和初始化有关。这里就不将它展开说了,有兴趣的可以去看看《STL源码剖析》这本书。
学习了vector,可以知道resize是一个改变vector的finish
- 如果传入new_size比有效元素少,就将后面的元素删除
- 如果传入的new_size比有效元素多,那么就在最后加上x
与resize相对应的就是reserve函数了,它是改变vector的end_of_storage
- 如果开辟的容量大于end_of_storage,那么就开辟一个n个空间的大小
- 然后将数据拷贝到新空间中,然后将旧空间释放掉
- 最后改变start的指向
但是这里注意一下,当发生数据拷贝的时候,是不能直接进行memcpy,要知道vector中存放的是一个T类型,如果T是string,那么当发生memcpy的时候就会发生浅拷贝,里面string指向的是一块释放了的空间。
接下来我们看一下insert函数
- 关于insert,首先我们要知道当进行插入操作时就有可能触发扩容,那么就有可能触发reserve函数,那么就有可能空间释放的问题,所以不能memmove。
所以,了解了一些特殊情况,就可以实现了。
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
: start(nullptr), finish(nullptr), end_of_storage(nullptr)
{
}
vector(size_t n, const T &value)
{
push_back(value);
}
explicit vector(size_t n)
{
for (int i = 0; i < n; i++)
{
push_back(T());
}
}
template<class InputIterator>
vector(InputIterator first,InputIterator last)
{
while(first != last)
{
push_back(*first);
++first;
}
}
vector(int n, const T &value = T())
{
resize(n, value);
}
vector(long n, const T &value = T())
{
resize(n, value);
}
// v2(v1)
vector(const vector<T> &v)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}
//v2 = v1
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
if (start)
{
delete[] start;
start = finish = end_of_storage = nullptr;
}
}
iterator begin()
{
return start;
}
const_iterator begin() const
{
return start;
}
iterator end()
{
return finish;
}
const_iterator end() const
{
return finish;
}
size_t size() const
{
return finish - start;
}
size_t max_size() const
{
return size_t(-1) / sizeof(T);
}
bool empty() const
{
return start == finish;
}
void swap(vector<T> &v)
{
::swap(start, v.start);
::swap(finish, v.finish);
::swap(end_of_storage, v.end_of_storage);
}
void resize(size_t n, T val = T())
{
// 如果n > size()就开空间,将val插入到后面
if (n > size())
{
reserve(n);
while (finish < start + n)
{
*finish = val;
++finish;
}
}
else
{
// 将有效空间变小
finish = start + n;
}
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t old = size();
// 申请新空间
T *tmp = new T[n];
if (start)
{
// memcpy(tmp,start,sizeof(T)*old);
// 之所以不能用memcpy,是因为如果是自定义类型,深拷贝的时候就会访问释放了的空间了
// 当然,这里可以用引用计数或者移动构造、移动赋值来搞定这个问题。
for (size_t i = 0; i < old; i++)
{
tmp[i] = start[i];
}
delete[] start;
}
start = tmp;
finish = start + old;
end_of_storage = start + n;
}
}
size_t capacity() const
{
return end_of_storage - begin();
}
void push_back(const T &x)
{
if (finish != end_of_storage)
{
*finish = x;
++finish;
}
else
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
*finish = x;
++finish;
}
}
void pop_back()
{
assert(size() > 0);
--finish;
}
iterator erase(iterator pos)
{
assert(pos >= start);
assert(pos < finish);
// 将pos之后的元素从后往前移动
iterator it = pos + 1;
while (it < finish)
{
*pos = *it;
it++;
}
--finish;
return pos;
}
iterator insert(iterator pos,const T& x)
{
assert(pos <= finish && pos >= start);
if(finish == end_of_storage)
{
size_t len = pos - start;
reserve(capacity() == 0 ? 4 : 2*capacity());
pos = start + len;
}
iterator end = finish;
while(pos < end)
{
*end = *(end-1);
--end;
}
*pos = x;
++finish;
return pos;
}
T &operator[](size_t n)
{
assert(n <= size());
return *(begin() + n);
}
const T &operator[](size_t pos) const
{
assert(pos >= start);
assert(pos < finish);
return start[pos];
}
private:
iterator start = nullptr;
iterator finish = nullptr;
iterator end_of_storage = nullptr;
};