当前位置:   article > 正文

【C++】vector_memcpy对vector赋值

memcpy对vector赋值

目录

1. vector介绍

1.1. 什么是vector

1.2. vector常用接口

2. vector常用接口模拟实现

2.1. 构造函数+析构函数+operator=

2.2. 迭代器

2.3. 其他常用接口

2.4. 迭代器失效 and memcpy问题

2.4.1.pos位置之前插入

2.4.2erase删除pos位置

2.4.3.memcpy问题


1. vector介绍

1.1. 什么是vector

1. vector是表示可变大小数组的序列容器。

2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。

3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大 小。

4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是 对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。

6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在 末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。

1.2. vector常用接口

构造函数说明
(★)vector()无参构造
vector(size_type n, const value_type& val = value_type())构造并初始化n个val
(★)vector (const vector& x)拷贝构造
vector (InputIterator first, InputIterator last)使用迭代器进行初始化构造

类成员函数说明
begin + end获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置reverse_iterator
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize改变vector的size
reserve改变vector的capacity
push_back尾插
pop_back尾删
find查找
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]重载[],使vector像数组一样访问
operator=重载=

2. vector常用接口模拟实现

对于像vector这样的模板容器,一般不采用声明和定义分离的形式实现!

2.1. 构造函数+析构函数+operator=

对于vector在STL源代码的SGI版本中,不同于string的实现,在定义类中的私有变量时不会定义一个指针两个整形的方法;而是定义三个指针,分别指向数组的开头,最后一个有效元素的下一个位置,数组末尾。

  1. #pragma once
  2. #include<iostream>
  3. #include<assert.h>
  4. #include<algorithm>
  5. using namespace std;
  6. namespace wt
  7. {
  8. template<class T>
  9. class vector
  10. {
  11. public:
  12. vector() // 空构造
  13. :_start(nullptr)
  14. ,_finish(nullptr)
  15. ,_endofstorage(nullptr)
  16. {}
  17. template<class Intputiterator> // 迭代器构造
  18. vector(Intputiterator first, Intputiterator last)
  19. :_start(nullptr)
  20. ,_finish(nullptr)
  21. ,_endofstorage(nullptr)
  22. {
  23. while (first != last)
  24. {
  25. push_back(*first); // 这里不能采用直接赋值指针的方法,需一个一个尾插
  26. ++first;
  27. }
  28. }
  29. void swap(vector<T>& v)
  30. {
  31. std::swap(_start, v._start);
  32. std::swap(_finish, v._finish);
  33. std::swap(_endofstorage, v._endofstorage);
  34. }
  35. vector(const vector<T>& v) //拷贝构造
  36. :_start(nullptr)
  37. , _finish(nullptr)
  38. , _endofstorage(nullptr)
  39. {
  40. vector<T> tmp(v.begin(), v.end()); // 利用迭代器构造临时对象
  41. swap(tmp); // 将临时对象与目标对象交换
  42. }
  43. ~vector() // 析构函数
  44. {
  45. delete[] _start;
  46. _start = _finish = _endofstorage = nullptr;
  47. }
  48. vector<T>& operator=(vector<T> v) // 赋值运算符重载
  49. {
  50. swap(v); // 这使用的是传值传参,所以直接将临时对象交换给目标对象即可
  51. return *this;
  52. }
  53. private:
  54. iterator _start; // 记录数组起始位置
  55. iterator _finish; // 记录最后一个有效元素下一个位置
  56. iterator _endofstorage; // 记录数组最大容量位置
  57. };

2.2. 迭代器

  1. typedef T* iterator;
  2. typedef const T* const_iterator;
  3. iterator begin()
  4. {
  5. return _start;
  6. }
  7. iterator end()
  8. {
  9. return _finish;
  10. }
  11. const_iterator begin()const
  12. {
  13. return _start;
  14. }
  15. const_iterator end()const
  16. {
  17. return _finish;
  18. }

2.3. 其他常用接口

  1. // 重载[]
  2. T& operator[](size_t i)
  3. {
  4. return *(_start + i);
  5. }
  6. const T& operator[](size_t i)const
  7. {
  8. return *(_start + i);
  9. }
  10. // 返回vector有效元素个数
  11. size_t size()const
  12. {
  13. return _finish - _start;
  14. }
  15. // 返回vector容量
  16. size_t capacity()const
  17. {
  18. return _endofstorage - _start;
  19. }
  20. // 改变vector有效元素个数
  21. void resize(size_t n, const T& val = T())
  22. {
  23. if (n < size())
  24. {
  25. _finish = _start + n;
  26. *_finish = val;
  27. }
  28. else
  29. {
  30. if (n > capacity()) // 容量不够需要扩容
  31. {
  32. reserve(n);
  33. }
  34. while (_finish != _start + n)
  35. {
  36. *_finish = val;
  37. ++_finish;
  38. }
  39. }
  40. }
  41. // 扩容
  42. void reserve(size_t n)
  43. {
  44. if (n > capacity())
  45. {
  46. T* tmp = new T[n]; // 创建临时数组
  47. if (_start)
  48. {
  49. memcpy(tmp, _start, sizeof(T) * size()); // 将原vector中的数据拷贝到tmp中
  50. delete[] _start; // 释放原vector
  51. }
  52. size_t sz = size();
  53. _start = tmp; // 将tmp赋值给vector
  54. _finish = _start + sz;
  55. _endofstorage = _start + n;
  56. }
  57. }
  58. // 尾插
  59. void push_back(T val)
  60. {
  61. if (_finish == _endofstorage) // 检查容量
  62. {
  63. reserve(capacity() == 0 ? 4 : capacity() * 2);
  64. }
  65. *_finish = val;
  66. ++_finish;
  67. }
  68. // 尾删
  69. void pop_back()
  70. {
  71. assert(_finish != _start); // vector不能为空
  72. --_finish;
  73. }
  74. // pos位置之前插入
  75. iterator insert(iterator pos, T val)
  76. {
  77. assert(pos >= _start);
  78. assert(pos <= _finish);
  79. if (_finish == _endofstorage)
  80. {
  81. size_t len = pos - _start;
  82. reserve(capacity() == 0 ? 4 : capacity() * 2);
  83. }
  84. iterator end = _finish + 1;
  85. while (end > pos)
  86. {
  87. *end = *(end - 1);
  88. --end;
  89. }
  90. *pos = val;
  91. return pos;
  92. }
  93. // 删除pos位置
  94. iterator erase(iterator pos)
  95. {
  96. assert(pos >= _start);
  97. assert(pos < _finish);
  98. iterator begin = pos;
  99. while (begin < _finish)
  100. {
  101. *begin = *(begin + 1);
  102. ++begin;
  103. },
  104. --_finish;
  105. return pos;
  106. }

2.4. 迭代器失效 and memcpy问题

2.4.1.pos位置之前插入

当使用insert时,如果涉及到增容,上面的insert就会出现问题。

例如:

  1. void test()
  2. {
  3. vector<int> v;
  4. v.push_back(1);
  5. v.push_back(2);
  6. v.push_back(3);
  7. v.push_back(4);
  8. v.insert(v.end(), 5);
  9. vector<int>::iterator it = v.begin();
  10. while (it != v.end())
  11. {
  12. cout << *it << " ";
  13. ++it;
  14. }
  15. cout << endl;
  16. }

因为,在增容之后,原来的空间被释放掉了,申请了新空间,而迭代器pos没有改变,依然指向原来的位置,就成了野指针!所以,在扩容之后,需改变pos指针!

  1. iterator insert(iterator pos, T val)
  2. {
  3. assert(pos >= _start);
  4. assert(pos <= _finish);
  5. if (_finish == _endofstorage)
  6. {
  7. size_t len = pos - _start;
  8. reserve(capacity() == 0 ? 4 : capacity() * 2);
  9. pos = _start + len; // 扩容后迭代器会失效
  10. }
  11. iterator end = _finish + 1;
  12. while (end > pos)
  13. {
  14. *end = *(end - 1);
  15. --end;
  16. }
  17. *pos = val;
  18. return pos;
  19. }

2.4.2erase删除pos位置

erase也会导致迭代器失效的问题:

  1. void test5()
  2. {
  3. vector<int> v;
  4. v.push_back(1);
  5. v.push_back(2);
  6. v.push_back(3);
  7. v.push_back(4);
  8. vector<int>::iterator it = v.begin();
  9. while (it != v.end())
  10. {
  11. if (*it % 2 == 0)
  12. {
  13. v.erase(it);
  14. }
  15. ++it;
  16. }
  17. while (it != v.end())
  18. {
  19. cout << *it << " ";
  20. ++it;
  21. }
  22. cout << endl;
  23. }

所以,在使用erase时,删除了元素就不移动迭代器!

  1. void test()
  2. {
  3. vector<int> v;
  4. v.push_back(1);
  5. v.push_back(2);
  6. v.push_back(3);
  7. v.push_back(4);
  8. vector<int>::iterator it = v.begin();
  9. while (it != v.end())
  10. {
  11. if (*it % 2 == 0)
  12. {
  13. v.erase(it);
  14. }
  15. else
  16. {
  17. ++it;
  18. }
  19. }
  20. for (auto& e : v)
  21. {
  22. cout << e << " ";
  23. }
  24. cout << endl;
  25. }

 

2.4.3.memcpy问题

在上面扩容时,使用了memcpy,在这里会存在一些问题。

例如:在vector中存放字符串

  1. void test6()
  2. {
  3. vector<string> v;
  4. v.push_back("111111111");
  5. v.push_back("111111111");
  6. v.push_back("111111111");
  7. v.push_back("111111111");
  8. for (auto& e : v)
  9. {
  10. cout << e << endl;
  11. }
  12. }

现在是没有问题的

再继续尾插时,就会开始增容,增容时会出现崩溃:

  1. void test6()
  2. {
  3. vector<string> v;
  4. v.push_back("111111111");
  5. v.push_back("111111111");
  6. v.push_back("111111111");
  7. v.push_back("111111111");
  8. v.push_back("111111111");
  9. for (auto& e : v)
  10. {
  11. cout << e << endl;
  12. }
  13. }

这是因为在扩容时使用了memcpy,而memcpy只是简单的浅拷贝,如果拷贝像string这样的类,在析构时会导致一块空间被析构两次!

所以扩容时不能使用memcpy,可通过其类型自己的赋值运算符重载完成拷贝。

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

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

闽ICP备14008679号