当前位置:   article > 正文

【C++】手撕 Vector类

【C++】手撕 Vector类

目录

1,vector类框架

2,vector ()

3,pinrt()

4,vector(int n, const T& value = T())

5,vector(const vector& v)

6,vector(InputIterator first, InputIterator last)

7,~vector()

8,iterator begin()

9,iterator end()

10,size() const

11,capacity() const

12,reserve(size_t n)

13,resize(size_t n, const T& value = T())

14,push_back(const T& x)

15,pop_back()

16,insert(iterator pos, const T& x)

17,erase(iterator pos)

18,empty()

19,operator[](size_t pos)

20,operator= (vector v)

21,总结


上面我们认识了 vector 类,有了一个大概的理解,下面我们来实现一下 vector 类的框架,来更好的熟悉 vector 类,也让我们对其有着更深的理解; 

1,vector类框架

我们先写一个 vector 类的基本框架;

  1. namespace newVector
  2. {
  3. template<class T>
  4. class vector
  5. {
  6. public:
  7. // Vector的迭代器是一个原生指针
  8. typedef T* iterator;
  9. private:
  10. iterator _start = nullptr; // 指向数据块的开始
  11. iterator _finish = nullptr; // 指向有效数据的尾
  12. iterator _endOfStorage = nullptr; // 指向存储容量的尾
  13. };
  14. }

vector 类里面是可以包含很多类型的,所以我们用模板来表示,以应用各种场景,vector 类里面多用迭代器的方式来表示,vector 的迭代器其实就是一个原生指针;

_start 指向数据块的开始,_finish 指向有效数据的尾,_endOfStorage 指向存储容量的尾;

2,vector ()

  1. vector()
  2. {}

因为我们在构造框架的时候已经给了缺省值,所以可以不用在写了;

可以看到这里已经初始化了;

3,pinrt()

就是打印输出嘛,方便后续测试;

  1. void pinrt()
  2. {
  3. for (size_t i = 0; i < size(); i++)
  4. {
  5. cout << _start[i] << " ";
  6. }
  7. }

4,vector(int n, const T& value = T())

我们都会用,初始化 n 个 value;

  1. vector(int n, const T& value = T())
  2. {
  3. reserve(n);
  4. for (size_t i = 0; i < n; i++)
  5. {
  6. push_back(value);
  7. }
  8. }

有人不知道缺省值给个 T()是什么意思,首先 T()是匿名对象,默认就是编译器对内置类型的初始化; 

测试一下:

  1. int main()
  2. {
  3. newVector::vector<int> v1(5, 8);
  4. v1.pinrt();
  5. return 0;
  6. }

现在我们不给初始化的值试试:

  1. int main()
  2. {
  3. newVector::vector<int> v1(5);
  4. v1.pinrt();
  5. return 0;
  6. }

 默认初始化为0;

5,vector(const vector<T>& v)

拷贝构造(深拷贝)

  1. vector(const vector<T>& v)
  2. {
  3. reserve(v.capacity());
  4. //memcpy(_start, v._start, sizeof(T)*v.size());
  5. for (size_t i = 0; i < v.size(); i++)
  6. {
  7. _start[i] = v._start[i];
  8. }
  9. _finish = _start + v.size();
  10. }

为什么不用 memcpy 呢,上面那个明显要便捷一点,因为 memcpy 是浅拷贝,如果遇到 T是string类的时候就会因为析构函数多次析构同一块空间而报错,而下面这个挨个赋值是深拷贝,他们两个 _start 不会指向同一块空间;

  1. int main()
  2. {
  3. newVector::vector<int> v1(5,8);
  4. newVector::vector<int> v2(v1);
  5. v2.pinrt();
  6. return 0;
  7. }

可以看到也是 OK 的;

6,vector(InputIterator first, InputIterator last)

这个要配合模板使用,这代表一个范围,只要是同类型的都可以;

  1. template<class InputIterator>
  2. vector(InputIterator first, InputIterator last)
  3. {
  4. reserve(last - first + 1);
  5. while (first != last)
  6. {
  7. push_back(*first);
  8. first++;
  9. }
  10. }

先扩容嘛,像这种区间都是左闭右开的,然后再挨个尾插即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. v1.pinrt();
  6. return 0;
  7. }

只要是同类型的都可以,像这种的数组都行;

7,~vector()

析构函数

  1. ~vector()
  2. {
  3. delete[] _start;
  4. _start = _finish = _endOfStorage = nullptr;
  5. }

delete 后面一定要带 [ ] ,因为析构的是一段连续的空间,就看做是数组即可,然后再将各个迭代器置空即可;

  1. int main()
  2. {
  3. newVector::vector<int> v1(5,8);
  4. v1.~vector();
  5. v1.pinrt();
  6. return 0;
  7. }

8,iterator begin()

指向第一个元素的迭代器

  1. iterator begin()
  2. {
  3. return _start;
  4. }

直接返回 _start 即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. cout << *v1.begin();
  6. return 0;
  7. }

9,iterator end()

指向最后一个元素下一个元素的迭代器

  1. iterator end()
  2. {
  3. return _finish;
  4. }

直接返回 _finish 即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. cout << *v1.end();
  6. return 0;
  7. }

直接随机数了;

换个思路,试一下:

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. cout << *(v1.end()-1);
  6. return 0;
  7. }

我们找他前一个迭代器,果然是最后一个数;

10,size() const

返回有效数据个数

  1. size_t size() const
  2. {
  3. return _finish - _start;
  4. }

直接迭代器相减即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. cout << v1.size();
  6. return 0;
  7. }

11,capacity() const

返回容量大小

  1. size_t capacity() const
  2. {
  3. return _endOfStorage - _start;
  4. }

直接迭代器相减即可,差值就是容量大小;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. cout << v1.capacity();
  6. return 0;
  7. }

12,reserve(size_t n)

扩容

  1. void reserve(size_t n)
  2. {
  3. if (n > capacity())
  4. {
  5. T* tmp = new T[n];
  6. size_t old = size();
  7. if (_start)
  8. {
  9. //memcpy(tmp, _start, old * sizeof(T));
  10. for (size_t i = 0; i < old; i++)
  11. {
  12. tmp[i] = _start[i];
  13. }
  14. delete[] _start;
  15. }
  16. _start = tmp;
  17. _finish = _start + old;
  18. _endOfStorage = _start + n;
  19. }
  20. }

当要扩容时才用的着,先判断,然后开辟一段需要的空间,之后就是拷贝赋值了,这里我们还是没有用 memcpy 因为是浅拷贝,然后再释放原空间,再将迭代器重新赋值即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. cout << v1.capacity() << endl;
  6. v1.reserve(20);
  7. cout << v1.capacity() << endl;
  8. return 0;
  9. }

一目了然;

13,resize(size_t n, const T& value = T())

更改有效数据个数,不够则填充,可以指定填充数据;

  1. void resize(size_t n, const T& value = T())
  2. {
  3. if (n < size())
  4. {
  5. _finish = _start + n;
  6. }
  7. else
  8. {
  9. if (n > capacity())
  10. {
  11. reserve(n);
  12. }
  13. while (_finish!=_start+n)
  14. {
  15. push_back(value);
  16. }
  17. }
  18. }

当缩减数据时直接把 _finish 往前移即可,当扩容时先扩容,然后进行填充;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr,arr+5);
  5. cout << v1.size() << endl;
  6. v1.resize(10);
  7. cout << v1.size() << endl;
  8. v1.resize(15, 6);
  9. cout << v1.size() << endl;
  10. v1.pinrt();
  11. return 0;
  12. }

可以看到,当我们不指定填充时默认填充0;

14,push_back(const T& x)

尾插

  1. void push_back(const T& x)
  2. {
  3. if (_finish == _endOfStorage)
  4. {
  5. size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
  6. reserve(newcapacity);
  7. }
  8. *_finish = x;
  9. _finish++;
  10. }

首先要检查是否需要扩容,然后赋值,将 _finish 往后移一位即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr, arr + 5);
  5. v1.push_back(6);
  6. v1.push_back(7);
  7. v1.pinrt();
  8. return 0;
  9. }

 插入十分成功;

15,pop_back()

尾删

  1. void pop_back()
  2. {
  3. assert(size() > 0);
  4. _finish--;
  5. }

像这种数组类型的,直接对其迭代器动手就行,直接 _finish 往前移一位即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr, arr + 5);
  5. v1.pop_back();
  6. v1.pop_back();
  7. v1.pinrt();
  8. return 0;
  9. }

删除非常顺利;

16,insert(iterator pos, const T& x)

指定位置插入

  1. iterator insert(iterator pos, const T& x)
  2. {
  3. assert(pos >= _start && pos <= _finish);
  4. size_t old = pos - _start;
  5. if (_finish == _endOfStorage)
  6. {
  7. size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
  8. reserve(newcapacity);
  9. }
  10. pos = _start + old;
  11. memcpy(pos + 1, pos, (_finish - pos) * sizeof(T));
  12. *pos = x;
  13. _finish++;
  14. return pos;
  15. }

这里会面临一个迭代器失效的问题,当扩容后,原本的 pos 还是指向旧空间就失效了,所以我们要更新 pos ;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr, arr + 5);
  5. auto pos = find(v1.begin(), v1.end(), 1);
  6. v1.insert(pos, 9);
  7. pos= find(v1.begin(), v1.end(), 5);
  8. v1.insert(pos, 9);
  9. v1.pinrt();
  10. return 0;
  11. }

完美插入;

17,erase(iterator pos)

擦除指定位置

  1. iterator erase(iterator pos)
  2. {
  3. assert(pos >= 0 && pos <= _finish);
  4. memcpy(pos, pos + 1, sizeof(T)*(_finish - pos));
  5. _finish--;
  6. return pos;
  7. }

先断言判断,然后直接往前移一位覆盖即可,再更新一下 _finish ;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr, arr + 5);
  5. auto pos = find(v1.begin(), v1.end(), 1);
  6. v1.erase(pos);
  7. pos = find(v1.begin(), v1.end(), 5);
  8. v1.erase(pos);
  9. v1.pinrt();
  10. return 0;
  11. }

也是成功擦除了;

18,empty()

判空

  1. bool empty()
  2. {
  3. return _start == _finish;
  4. }

当为空时返回真,反之亦然;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr, arr + 5);
  5. cout << v1.empty() << endl;
  6. newVector::vector<int> v2;
  7. cout << v2.empty();
  8. return 0;
  9. }

19,operator[](size_t pos)

返回下标对应的值;

  1. T& operator[](size_t pos)
  2. {
  3. assert(pos >= 0 && pos <= size());
  4. return _start[pos];
  5. }

直接像数组一样取值返回即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr, arr + 5);
  5. cout << v1[0] << " " << v1[4];
  6. return 0;
  7. }

写法也是跟数组一样简单;

20,operator= (vector<T> v)

赋值,深拷贝

  1. void swap(vector<T>& v)
  2. {
  3. std::swap(_start, v._start);
  4. std::swap(_finish, v._finish);
  5. std::swap(_endOfStorage, v._endOfStorage);
  6. }
  7. vector<T>& operator= (vector<T> v)
  8. {
  9. swap(v);
  10. return *this;
  11. }

直接一手交换即可;

  1. int main()
  2. {
  3. int arr[5] = { 1,2,3,4,5 };
  4. newVector::vector<int> v1(arr, arr + 5);
  5. newVector::vector<int> v2 = v1;
  6. v2.pinrt();
  7. return 0;
  8. }

 

21,总结

我们就先搞一个大概的,其中还有很多分支,比如我们写的是擦除某个数据,其实也可以擦除某个范围,这些就靠大家去摸索,查阅文档了;

vector类的实现就到这里了;

加油!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/424950
推荐阅读
相关标签
  

闽ICP备14008679号