当前位置:   article > 正文

【C++航海王:追寻罗杰的编程之路】vector

【C++航海王:追寻罗杰的编程之路】vector

目录

1 -> vector的介绍及使用

1.1 -> vector的介绍

1.2 -> vector的使用

1.2.1 -> vector的介绍

1.2.2 -> vector iterator的使用

1.2.3 -> vector空间增长问题

1.2.4 -> vector的增删查改

1.2.5 -> vector迭代器失效问题

2 -> vector的深度剖析及模拟实现

2.1 -> vector的模拟实现

2.2 -> 使用memcpy拷贝问题

2.3 -> 动态二维数组理解


1 -> vector的介绍及使用

1.1 -> vector的介绍

vector的文档介绍

  1. vector是表示可变大小数组的序列容器;
  2. 像数组一样,vector也采用的连续存储空间来存储元素。也就意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理;
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器时,vector并不会每次都重新分配大小;
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的;
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长;
  6. 与其他动态序列容器相比(deque, list and forward_list),vector在访问元素时更加高效,在,末尾添加和删除元素相对高效。对于其他不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。

使用STL的三个境界:能用、明理、能扩展。

1.2 -> vector的使用

1.2.1 -> vector的介绍

构造函数声明接口说明
vector()无参构造
vector(size_type n, const value_type& val = value_type())构造并初始化n个val
vector(const vector& x)拷贝构造
vector(Inputlterator first, Inputlterator last)使用迭代器进行初始化构造
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. int TestVector1()
  6. {
  7. vector<int> first;
  8. vector<int> second(4, 100);
  9. vector<int> third(second.begin(), second.end());
  10. vector<int> fourth(third);
  11. int myints[] = { 16,2,77,29 };
  12. vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));
  13. cout << "The contents of fifth are:";
  14. for (vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
  15. cout << ' ' << *it;
  16. cout << endl;
  17. return 0;
  18. }
  19. int main()
  20. {
  21. TestVector1();
  22. return 0;
  23. }

1.2.2 -> vector iterator的使用

iterator的使用接口说明
begin + end获取第一个数据位置的iterator / const_iterator,获取最后一个数据的下一个位置的iterator / const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iteratorreverse_iterator

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. void PrintVector(const vector<int>& v)
  6. {
  7. // const对象使用const迭代器进行遍历打印
  8. vector<int>::const_iterator it = v.begin();
  9. while (it != v.end())
  10. {
  11. cout << *it << " ";
  12. ++it;
  13. }
  14. cout << endl;
  15. }
  16. void TestVector2()
  17. {
  18. vector<int> v;
  19. v.push_back(1);
  20. v.push_back(2);
  21. v.push_back(3);
  22. v.push_back(4);
  23. // 使用迭代器进行遍历打印
  24. vector<int>::iterator it = v.begin();
  25. while (it != v.end())
  26. {
  27. cout << *it << " ";
  28. ++it;
  29. }
  30. cout << endl;
  31. // 使用迭代器进行修改
  32. it = v.begin();
  33. while (it != v.end())
  34. {
  35. *it *= 2;
  36. ++it;
  37. }
  38. // 使用反向迭代器进行遍历再打印
  39. // vector<int>::reverse_iterator rit = v.rbegin();
  40. auto rit = v.rbegin();
  41. while (rit != v.rend())
  42. {
  43. cout << *rit << " ";
  44. ++rit;
  45. }
  46. cout << endl;
  47. PrintVector(v);
  48. }
  49. int main()
  50. {
  51. TestVector2();
  52. return 0;
  53. }

1.2.3 -> vector空间增长问题

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize改变vector的size
reverse改变vector的capacity
  • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。不要固化认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL;
  • reverse只负责开辟空间,如果确定知道需要用多少空间,reverse可以缓解vector增容的代价缺陷问题;
  • resize在开空间的同时还会进行初始化,影响size。

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. // 将有效元素个数设置为n个,如果时增多时,增多的元素使用data进行填充
  6. // 注意:resize在增多元素个数时可能会扩容
  7. void TestVector3()
  8. {
  9. vector<int> v;
  10. for (int i = 1; i < 10; i++)
  11. v.push_back(i);
  12. v.resize(5);
  13. v.resize(8, 100);
  14. v.resize(12);
  15. cout << "v contains:";
  16. for (size_t i = 0; i < v.size(); i++)
  17. cout << ' ' << v[i];
  18. cout << endl;
  19. }
  20. // 测试vector的默认扩容机制
  21. // vs:按照1.5倍方式扩容
  22. // linux:按照2倍方式扩容
  23. void TestVectorExpand()
  24. {
  25. size_t sz;
  26. vector<int> v;
  27. sz = v.capacity();
  28. cout << "making v grow:" << endl;
  29. for (int i = 0; i < 100; ++i)
  30. {
  31. v.push_back(i);
  32. if (sz != v.capacity())
  33. {
  34. sz = v.capacity();
  35. cout << "capacity changed: " << sz << endl;
  36. }
  37. }
  38. }
  39. // 往vecotr中插入元素时,如果大概已经知道要存放多少个元素
  40. // 可以通过reserve方法提前将容量设置好,避免边插入边扩容效率低
  41. void TestVectorExpandOP()
  42. {
  43. vector<int> v;
  44. size_t sz = v.capacity();
  45. // 提前将容量设置好,可以避免一遍插入一遍扩容
  46. v.reserve(100);
  47. cout << "making bar grow:" << endl;
  48. for (int i = 0; i < 100; ++i)
  49. {
  50. v.push_back(i);
  51. if (sz != v.capacity())
  52. {
  53. sz = v.capacity();
  54. cout << "capacity changed: " << sz << endl;
  55. }
  56. }
  57. }
  58. int main()
  59. {
  60. TestVector3();
  61. TestVectorExpand();
  62. TestVectorExpandOP();
  63. return 0;
  64. }

1.2.4 -> vector的增删查改

vector增删查改接口说明
push_back尾插
pop_back尾删
find查找
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]像数组一样访问
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. void TestVector4()
  6. {
  7. vector<int> v;
  8. v.push_back(1);
  9. v.push_back(2);
  10. v.push_back(3);
  11. v.push_back(4);
  12. auto it = v.begin();
  13. while (it != v.end())
  14. {
  15. cout << *it << " ";
  16. ++it;
  17. }
  18. cout << endl;
  19. v.pop_back();
  20. v.pop_back();
  21. it = v.begin();
  22. while (it != v.end())
  23. {
  24. cout << *it << " ";
  25. ++it;
  26. }
  27. cout << endl;
  28. }
  29. // 任意位置插入:insert和erase,以及查找find
  30. // 注意find不是vector自身提供的方法,是STL提供的算法
  31. void TestVector5()
  32. {
  33. // 使用列表方式初始化,C++11新语法
  34. vector<int> v{ 1, 2, 3, 4 };
  35. // 在指定位置前插入值为val的元素,比如:3之前插入30,如果没有则不插入
  36. // 1. 先使用find查找3所在位置
  37. // 注意:vector没有提供find方法,如果要查找只能使用STL提供的全局find
  38. auto pos = find(v.begin(), v.end(), 3);
  39. if (pos != v.end())
  40. {
  41. // 2. 在pos位置之前插入30
  42. v.insert(pos, 30);
  43. }
  44. vector<int>::iterator it = v.begin();
  45. while (it != v.end())
  46. {
  47. cout << *it << " ";
  48. ++it;
  49. }
  50. cout << endl;
  51. pos = find(v.begin(), v.end(), 3);
  52. // 删除pos位置的数据
  53. v.erase(pos);
  54. it = v.begin();
  55. while (it != v.end())
  56. {
  57. cout << *it << " ";
  58. ++it;
  59. }
  60. cout << endl;
  61. }
  62. // operator[]+index 和 C++11中vector的新式for+auto的遍历
  63. // vector使用这两种遍历方式是比较便捷的。
  64. void TestVector6()
  65. {
  66. vector<int> v{ 1, 2, 3, 4 };
  67. // 通过[]读写第0个位置。
  68. v[0] = 10;
  69. cout << v[0] << endl;
  70. // 1. 使用for+[]小标方式遍历
  71. for (size_t i = 0; i < v.size(); ++i)
  72. cout << v[i] << " ";
  73. cout << endl;
  74. vector<int> swapv;
  75. swapv.swap(v);
  76. cout << "v data:";
  77. for (size_t i = 0; i < v.size(); ++i)
  78. cout << v[i] << " ";
  79. cout << endl;
  80. // 2. 使用迭代器遍历
  81. cout << "swapv data:";
  82. auto it = swapv.begin();
  83. while (it != swapv.end())
  84. {
  85. cout << *it << " ";
  86. ++it;
  87. }
  88. // 3. 使用范围for遍历
  89. for (auto x : v)
  90. cout << x << " ";
  91. cout << endl;
  92. }
  93. int main()
  94. {
  95. TestVector4();
  96. TestVector5();
  97. TestVector6();
  98. return 0;
  99. }

1.2.5 -> vector迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。

对于vector可能会导致其迭代器失效的操作有:

1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reverse、insert、assign、push_back等。

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. int main()
  6. {
  7. vector<int> v{ 1,2,3,4,5,6 };
  8. auto it = v.begin();
  9. // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
  10. // v.resize(100, 8);
  11. // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
  12. // v.reserve(100);
  13. // 插入元素期间,可能会引起扩容,而导致原空间被释放
  14. // v.insert(v.begin(), 0);
  15. // v.push_back(8);
  16. // 给vector重新赋值,可能会引起底层容量改变
  17. v.assign(100, 8);
  18. /*
  19. 出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,
  20. 而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的
  21. 空间,而引起代码运行时崩溃。
  22. 解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新
  23. 赋值即可。
  24. */
  25. while (it != v.end())
  26. {
  27. cout << *it << " ";
  28. ++it;
  29. }
  30. cout << endl;
  31. return 0;
  32. }

2. 指定位置元素的删除 -> erase

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. int main()
  6. {
  7. int a[] = { 1, 2, 3, 4 };
  8. vector<int> v(a, a + sizeof(a) / sizeof(int));
  9. // 使用find查找3所在位置的iterator
  10. vector<int>::iterator pos = find(v.begin(), v.end(), 3);
  11. // 删除pos位置的数据,导致pos迭代器失效。
  12. v.erase(pos);
  13. cout << *pos << endl; // 此处会导致非法访问
  14. return 0;
  15. }

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上迭代器不会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

3. 注意:Linux下,g++编译器对迭代器失效的检测不是非常严格,处理也没有vs下极端。

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. // 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
  6. int main()
  7. {
  8. vector<int> v{ 1,2,3,4,5 };
  9. for (size_t i = 0; i < v.size(); ++i)
  10. cout << v[i] << " ";
  11. cout << endl;
  12. auto it = v.begin();
  13. cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
  14. // 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效
  15. v.reserve(100);
  16. cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
  17. // 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会
  18. // 虽然可能运行,但是输出的结果是不对的
  19. while (it != v.end())
  20. {
  21. cout << *it << " ";
  22. ++it;
  23. }
  24. cout << endl;
  25. return 0;
  26. }

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. // 2. erase删除任意位置代码后,linux下迭代器并没有失效
  6. // 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
  7. int main()
  8. {
  9. vector<int> v{ 1,2,3,4,5 };
  10. vector<int>::iterator it = find(v.begin(), v.end(), 3);
  11. v.erase(it);
  12. cout << *it << endl;
  13. while (it != v.end())
  14. {
  15. cout << *it << " ";
  16. ++it;
  17. }
  18. cout << endl;
  19. return 0;
  20. }
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <vector>
  4. using namespace std;
  5. // 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
  6. // 此时迭代器是无效的,++it导致程序崩溃
  7. int main()
  8. {
  9. vector<int> v{ 1,2,3,4,5 };
  10. auto it = v.begin();
  11. while (it != v.end())
  12. {
  13. if (*it % 2 == 0)
  14. v.erase(it);
  15. ++it;
  16. }
  17. for (auto e : v)
  18. cout << e << " ";
  19. cout << endl;
  20. return 0;
  21. }

从上述三个例子可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃。

4. 与vector类似,string在插入+扩容操作+erase后,迭代器也会失效

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. using namespace std;
  4. void TestString()
  5. {
  6. string s("hello");
  7. auto it = s.begin();
  8. // 放开之后代码会崩溃,因为resize到20string会进行扩容
  9. // 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
  10. // 后序打印时,再访问it指向的空间程序就会崩溃
  11. //s.resize(20, '!');
  12. while (it != s.end())
  13. {
  14. cout << *it;
  15. ++it;
  16. }
  17. cout << endl;
  18. it = s.begin();
  19. while (it != s.end())
  20. {
  21. it = s.erase(it);
  22. // 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
  23. // it位置的迭代器就失效了
  24. // s.erase(it);
  25. ++it;
  26. }
  27. }
  28. int main()
  29. {
  30. TestString();
  31. return 0;
  32. }

迭代器失效的解决方法:在使用前,对迭代器重新赋值即可。

2 -> vector的深度剖析及模拟实现

2.1 -> vector的模拟实现

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <iostream>
  3. #include <assert.h>
  4. using namespace std;
  5. namespace fyd
  6. {
  7. template<class T>
  8. class vector
  9. {
  10. public:
  11. // Vector的迭代器是一个原生指针
  12. typedef T* iterator;
  13. typedef const T* const_iterator;
  14. ///
  15. // 构造和销毁
  16. vector()
  17. : _start(nullptr)
  18. , _finish(nullptr)
  19. , _endOfStorage(nullptr)
  20. {}
  21. vector(size_t n, const T& value = T())
  22. : _start(nullptr)
  23. , _finish(nullptr)
  24. , _endOfStorage(nullptr)
  25. {
  26. reserve(n);
  27. while (n--)
  28. {
  29. push_back(value);
  30. }
  31. }
  32. /*
  33. * 理论上将,提供了vector(size_t n, const T& value = T())之后
  34. * vector(int n, const T& value = T())就不需要提供了,但是对于:
  35. * vector<int> v(10, 5);
  36. * 编译器在编译时,认为T已经被实例化为int,而105编译器会默认其为int类型
  37. * 就不会走vector(size_t n, const T& value = T())这个构造方法,
  38. * 最终选择的是:vector(InputIterator first, InputIterator last)
  39. * 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int
  40. * 但是105根本不是一个区间,编译时就报错了
  41. * 故需要增加该构造方法
  42. */
  43. vector(int n, const T& value = T())
  44. : _start(new T[n])
  45. , _finish(_start + n)
  46. , _endOfStorage(_finish)
  47. {
  48. for (int i = 0; i < n; ++i)
  49. {
  50. _start[i] = value;
  51. }
  52. }
  53. // 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
  54. // 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器
  55. template<class InputIterator>
  56. vector(InputIterator first, InputIterator last)
  57. {
  58. while (first != last)
  59. {
  60. push_back(*first);
  61. ++first;
  62. }
  63. }
  64. vector(const vector<T>& v)
  65. : _start(nullptr)
  66. , _finish(nullptr)
  67. , _endOfStorage(nullptr)
  68. {
  69. reserve(v.capacity());
  70. iterator it = begin();
  71. const_iterator vit = v.cbegin();
  72. while (vit != v.cend())
  73. {
  74. *it++ = *vit++;
  75. }
  76. _finish = it;
  77. }
  78. vector<T>& operator=(vector<T> v)
  79. {
  80. swap(v);
  81. return *this;
  82. }
  83. ~vector()
  84. {
  85. if (_start)
  86. {
  87. delete[] _start;
  88. _start = _finish = _endOfStorage = nullptr;
  89. }
  90. }
  91. /
  92. // 迭代器相关
  93. iterator begin()
  94. {
  95. return _start;
  96. }
  97. iterator end()
  98. {
  99. return _finish;
  100. }
  101. const_iterator cbegin() const
  102. {
  103. return _start;
  104. }
  105. const_iterator cend() const
  106. {
  107. return _finish;
  108. }
  109. //
  110. // 容量相关
  111. size_t size() const
  112. {
  113. return _finish - _start;
  114. }
  115. size_t capacity() const
  116. {
  117. return _endOfStorage - _start;
  118. }
  119. bool empty() const
  120. {
  121. return _start == _finish;
  122. }
  123. void reserve(size_t n)
  124. {
  125. if (n > capacity())
  126. {
  127. size_t oldSize = size();
  128. // 1. 开辟新空间
  129. T* tmp = new T[n];
  130. if (_start)
  131. {
  132. for (size_t i = 0; i < oldSize; ++i)
  133. tmp[i] = _start[i];
  134. // 3. 释放旧空间
  135. delete[] _start;
  136. }
  137. _start = tmp;
  138. _finish = _start + oldSize;
  139. _endOfStorage = _start + n;
  140. }
  141. }
  142. void resize(size_t n, const T& value = T())
  143. {
  144. // 1.如果n小于当前的size,则数据个数缩小到n
  145. if (n <= size())
  146. {
  147. _finish = _start + n;
  148. return;
  149. }
  150. // 2.空间不够则增容
  151. if (n > capacity())
  152. reserve(n);
  153. // 3.将size扩大到n
  154. iterator it = _finish;
  155. _finish = _start + n;
  156. while (it != _finish)
  157. {
  158. *it = value;
  159. ++it;
  160. }
  161. }
  162. ///
  163. // 元素访问
  164. T& operator[](size_t pos)
  165. {
  166. assert(pos < size());
  167. return _start[pos];
  168. }
  169. const T& operator[](size_t pos)const
  170. {
  171. assert(pos < size());
  172. return _start[pos];
  173. }
  174. T& front()
  175. {
  176. return *_start;
  177. }
  178. const T& front()const
  179. {
  180. return *_start;
  181. }
  182. T& back()
  183. {
  184. return *(_finish - 1);
  185. }
  186. const T& back()const
  187. {
  188. return *(_finish - 1);
  189. }
  190. /
  191. // vector的修改操作
  192. void push_back(const T& x)
  193. {
  194. insert(end(), x);
  195. }
  196. void pop_back()
  197. {
  198. erase(end() - 1);
  199. }
  200. void swap(vector<T>& v)
  201. {
  202. std::swap(_start, v._start);
  203. std::swap(_finish, v._finish);
  204. std::swap(_endOfStorage, v._endOfStorage);
  205. }
  206. iterator insert(iterator pos, const T& x)
  207. {
  208. assert(pos <= _finish);
  209. // 空间不够先进行增容
  210. if (_finish == _endOfStorage)
  211. {
  212. //size_t size = size();
  213. size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
  214. reserve(newCapacity);
  215. // 如果发生了增容,需要重置pos
  216. pos = _start + size();
  217. }
  218. iterator end = _finish - 1;
  219. while (end >= pos)
  220. {
  221. *(end + 1) = *end;
  222. --end;
  223. }
  224. *pos = x;
  225. ++_finish;
  226. return pos;
  227. }
  228. // 返回删除数据的下一个数据
  229. // 方便解决:一边遍历一边删除的迭代器失效问题
  230. iterator erase(iterator pos)
  231. {
  232. // 挪动数据进行删除
  233. iterator begin = pos + 1;
  234. while (begin != _finish)
  235. {
  236. *(begin - 1) = *begin;
  237. ++begin;
  238. }
  239. --_finish;
  240. return pos;
  241. }
  242. private:
  243. iterator _start; // 指向数据块的开始
  244. iterator _finish; // 指向有效数据的尾
  245. iterator _endOfStorage; // 指向存储容量的尾
  246. };
  247. }
  248. /// /
  249. /// 测试
  250. void TestVector1()
  251. {
  252. fyd::vector<int> v1;
  253. fyd::vector<int> v2(10, 5);
  254. int array[] = { 1,2,3,4,5 };
  255. fyd::vector<int> v3(array, array + sizeof(array) / sizeof(array[0]));
  256. fyd::vector<int> v4(v3);
  257. for (size_t i = 0; i < v2.size(); ++i)
  258. {
  259. cout << v2[i] << " ";
  260. }
  261. cout << endl;
  262. auto it = v3.begin();
  263. while (it != v3.end())
  264. {
  265. cout << *it << " ";
  266. ++it;
  267. }
  268. cout << endl;
  269. for (auto e : v4)
  270. {
  271. cout << e << " ";
  272. }
  273. cout << endl;
  274. }
  275. void TestVector2()
  276. {
  277. fyd::vector<int> v;
  278. v.push_back(1);
  279. v.push_back(2);
  280. v.push_back(3);
  281. v.push_back(4);
  282. v.push_back(5);
  283. cout << v.size() << endl;
  284. cout << v.capacity() << endl;
  285. cout << v.front() << endl;
  286. cout << v.back() << endl;
  287. cout << v[0] << endl;
  288. for (auto e : v)
  289. {
  290. cout << e << " ";
  291. }
  292. cout << endl;
  293. v.pop_back();
  294. v.pop_back();
  295. for (auto e : v)
  296. {
  297. cout << e << " ";
  298. }
  299. cout << endl;
  300. v.insert(v.begin(), 0);
  301. for (auto e : v)
  302. {
  303. cout << e << " ";
  304. }
  305. cout << endl;
  306. v.erase(v.begin() + 1);
  307. for (auto e : v)
  308. {
  309. cout << e << " ";
  310. }
  311. cout << endl;
  312. }
  313. int main()
  314. {
  315. TestVector1();
  316. TestVector2();
  317. return 0;
  318. }

2.2 -> 使用memcpy拷贝问题

 在vector模拟实现的reverse接口中,若使用memcpy进行拷贝,以下代码会发生什么问题?

  1. int main()
  2. {
  3. fyd::vector<std::string> v;
  4. v.push_back("1111");
  5. v.push_back("2222");
  6. v.push_back("3333");
  7. return 0;
  8. }

分析:

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中;
  2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

2.3 -> 动态二维数组理解

  1. // 以杨辉三角的前n行为例:假设n为5
  2. void TestVector3(size_t n)
  3. {
  4. // 使用vector定义二维数组vv,vv中的每个元素都是vector<int>
  5. fyd::vector<fyd::vector<int>> vv(n);
  6. // 将二维数组每一行中的vecotr<int>中的元素全部设置为1
  7. for (size_t i = 0; i < n; ++i)
  8. vv[i].resize(i + 1, 1);
  9. // 给杨辉三角出第一列和对角线的所有元素赋值
  10. for (int i = 2; i < n; ++i)
  11. {
  12. for (int j = 1; j < i; ++j)
  13. {
  14. vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
  15. }
  16. }
  17. }

fyd::vector<fyd::vector<int>> vv(n);构造一个vv动态二维数组,vv中总共有n个元素,每个元素都是vector类型的,每行没有包含任何元素。

填充完成后:

使用标准库中vector构建动态二维数组时与上图一致。


感谢大佬们的支持!!!

互三啦!!!

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

闽ICP备14008679号