vector各种接口的模拟实现
写在前面:
其实相比string,vector的接口就要简单许多而且更加规范清晰,但是这里也有我们需要注意的问题,一个是浅拷贝的问题,另一个就是迭代器失效
首先是浅拷贝,这里最容易给自己挖坑的就是在reserve()函数的时候,直接用memcpy()去一个字节一个字节的拷贝,一但调用时使用的是自定义类型,就会因为浅拷贝崩溃(例如vector<string>);其次是迭代器失效,一个是insert,一个是erase,insert之后,除非更新,应当认为迭代器已经失效,比如vs编译器会直接报错,再就是erase,也会出现相同的问题,不过不同点是,insert不一定会迭代器失效,但是erase在不同容器中几乎都会失效
目录
一、基本框架
二、各种构造函数与析构函数
三、增删查改与扩容
一、基本框架
namespace wjl
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;int capacity = _finish - _start;}
}
二、各种构造函数与析构函数
//vector()//{//这里什么都不需要写,因为编译器一定会走初始化列表//而下面声明的地方我们又给了缺省值,所以已经完成了初始化列表的初始化//}//C++11 强制生成默认构造vector() = default;//只要写了任意的构造,就不会在生成拷贝构造vector(const vector<T>& v){reserve(v.size());for (auto& e : v){push_back(e);}}//输入一个数的和他的个数,将整个vector都用这个数填充
vector(int n, const T& val = T())
{reserve(n);while (n--){push_back(val);}
}~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}
三、增删查改与扩容
void clear()
{_finish = _start;
}vector<T>& operator= (const vector<T>& v)
{clear();if (this != &v){reserve(v.size());for (auto& e : v){push_back(e);}}return *this;
}//以下部分为现代写法,但是传统的可读性更好,选择哪种看个人需求
/*void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}*//*vector<T>& operator= (vector<T> v)
{swap(v);return *this;
}*/iterator begin()
{return _start;
}iterator end()
{return _finish;
}const_iterator begin() const
{return _start;
}const_iterator end() const//意味着传过来的是const对象
{return _finish;
}//这里就是将size变为n,并将变动的空间变为给定的值
void resize(size_t n, T val = T())
{if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;_finish++;}}
}int size()const
{return _finish - _start;
}int capacity()const
{return _end_of_storage - _start;
}//这里是第一个要注意的点
void reserve(size_t n)
{if (n > capacity()){//其实这里就是要注意size的更新问题int old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, size() * sizeof(T));for (int i = 0; i < size(); i++){//这里调用的是string的赋值,完成了深拷贝//内置类型不要紧,但是一但用了自定义类型就会因为浅拷贝导致程序崩溃tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = _start + old_size;_end_of_storage = tmp + n;}
}void push_back(const T& x)
{if (_finish != _end_of_storage){*_finish = x;_finish++;}else{reserve(capacity() == 0 ? 4 : capacity() * 2);*_finish = x;_finish++;}
}T& operator[](int i)
{assert(i < size());return _start[i];
}const T& operator[](int i) const
{assert(i < size());return _start[i];
}bool empty()const
{return _start == _finish;
}void pop_back()
{assert(!empty());--_finish;
}iterator& erase(iterator pos)
{//进行这样的erase操作之后,vs也会认为pos已经失效assert(pos >= _start);assert(pos <= _finish);iterator it = pos + 1;while (it < end()){*(it - 1) = *(it);it++;}_finish--;return pos;
}void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);//迭代器失效——基本上所有容器都会有erase迭代器失效的问题,但是insert不一定// 1、类似野指针 —— 在扩容之后// 2、位置意义已经变了//但是vs编译器进行了强制检查,insert之后访问会直接报错if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;
}