当前位置:   article > 正文

【C++】vector_vector结构体数组直接赋值会不会内存泄漏

vector结构体数组直接赋值会不会内存泄漏

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

本篇文章将讲解vector使用和模拟实现。


1.vector的使用

1.1vector的定义

构造函数声明接口说明
vector()无参构造
vecor(size_t tyoe n,const value_type& val = value_type())构造并初始化n个val
vector(const vercort& x)拷贝构造
vector(inputiterator fist,inputoterator last)使用迭代器进行初始化构造

1.2 vector iterator的使用

begin+end获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置 的iterator/const_iterator
rbegin+rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator

1.3 vector 空间增长问题

size获取数据个数
capacity获取容量大小
empty判断是狗为空
resize改变vector的size
reserve改变vector的capacity

注意:

1、vs下capacity是按1.5倍增长的,g++是按2倍增长的。

2、reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。

3、resize在开空间的同时还会进行初始化,影响size。

1.4vecotr增删改查

push_back尾插
pop_back尾删
find查找。(注意这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]像数组一样访问

1.5迭代器失效

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

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

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

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

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

  1. int a[] = { 1, 2, 3, 4 };
  2. vector<int> v(a, a + sizeof(a) / sizeof(int));
  3. // 使用find查找3所在位置的iterator
  4. vector<int>::iterator pos = find(v.begin(), v.end(), 3);
  5. // 删除pos位置的数据,导致pos迭代器失效。
  6. v.erase(pos);
  7. cout << *pos << endl; // 此处会导致非法访问

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

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

  1. string s("hello");
  2. auto it = s.begin();
  3. // 放开之后代码会崩溃,因为resize到20string会进行扩容
  4. // 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
  5. // 后序打印时,再访问it指向的空间程序就会崩溃
  6. //s.resize(20, '!');
  7. while (it != s.end())
  8. {
  9. cout << *it;
  10. ++it;
  11. }
  12. cout << endl;
  13. it = s.begin();
  14. while (it != s.end())
  15. {
  16. it = s.erase(it);
  17. // 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
  18. // it位置的迭代器就失效了
  19. // s.erase(it);
  20. ++it;
  21. }

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

2.vector模拟实现

在源码中,vector只有三个成员变量

2.1构造析构函数

  1. //无参构造
  2. vector()
  3. :_start(nullptr)
  4. ,_finish(nullptr)
  5. ,_endOfStorage(nullptr)
  6. {}
  7. 用两个迭代区间构造
  8. template<typename inputiterator>
  9. vector(inputiterator first, inputiterator last)
  10. : _start(nullptr)
  11. , _finish(nullptr)
  12. , _endOfStorage(nullptr)
  13. {
  14. while (first != last)
  15. {
  16. push_back(*first);
  17. first++;
  18. }
  19. }
  20. void swap(vector<T>& v)
  21. {
  22. std::swap(_start, v._start);
  23. std::swap(_finish, v._finish);
  24. std::swap(_endOfStorage, v._endOfStorage);
  25. }
  26. //拷贝构造
  27. vector(const vector<T>& v)
  28. : _start(nullptr)
  29. , _finish(nullptr)
  30. , _endOfStorage(nullptr)
  31. {
  32. vector<T> temp(v.begin(),v.end());
  33. swap(temp);
  34. }
  35. ~vector()
  36. {
  37. if (_start)
  38. {
  39. delete[] _start;
  40. _start = _finish = _endOfStorage = nullptr;
  41. }
  42. }

2.2 reserve

  1. void reserve(size_t n)
  2. {
  3. size_t sz = size();
  4. if (n > capacity())
  5. {
  6. T* temp = new T[n];
  7. if (_start)
  8. {
  9. //memcpy(temp,_start,size()*sizeof(T))
  10. //如果用memcpy来将原来空间的值拷贝给新的空间,是浅拷贝,当拷贝的内容为内置类型时,不会有问题,但是当拷贝的类容是自定义类型时就会出问题
  11. for (int i = 0; i < sz; i++)
  12. {
  13. temp[i] = _start[i];
  14. }
  15. delete[] _start;
  16. }
  17. _start = temp;
  18. _finish = _start + sz;
  19. _endOfStorage = _start + n;
  20. }

2.3insert

  1. iterator insert(iterator pos, T val)//返回指向插入元素的迭代器
  2. {
  3. int n = pos - _start;
  4. assert(pos >= _start && pos <= _finish);
  5. if (_finish == _endOfStorage)
  6. {
  7. size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
  8. reserve(newCapacity);
  9. pos = _start + n;
  10. }
  11. iterator end = _finish;
  12. while (end != pos)
  13. {
  14. *(end+1) = *end;
  15. end--;
  16. }
  17. *pos = val;
  18. _finish++;
  19. return pos;
  20. }

vector中insert函数的逻辑很简单,但是要注意它需要返回一个迭代器,为了解决迭代器失效问题。

比如,现在有一个vector,里面存的元素是1 2 3 4 5 

现在想在偶数前插入一个0

  1. ldx::vector<int>::iterator it = v.begin();
  2. while (it != v.end())
  3. {
  4. if (*it % 2 == 0)//偶数
  5. {
  6. v.insert(it, 0);
  7. }
  8. it++
  9. }

假设像上面这么写,就会出现一些问题

 并能像我们预期的那样在偶数前插入0

正确的写法

  1. ldx::vector<int>::iterator it = v.begin();
  2. while (it != v.end())
  3. {
  4. if (*it % 2 == 0)//偶数
  5. {
  6. it=v.insert(it, 0);
  7. it++;
  8. }
  9. it++;
  10. }

2.4erase

  1. iterator erase(iterator pos)
  2. {
  3. assert(pos >= _start && pos < _finish);
  4. iterator temp = pos + 1;
  5. while (temp < _finish)
  6. {
  7. *(temp-1) = *temp;
  8. temp++;
  9. }
  10. _finish--;
  11. return pos;
  12. }

erase同样需要考虑迭代器失效的问题

例如在[1 2  4 5]中,为偶数的元素

  1. ldx::vector<int>::iterator it = v.begin();
  2. while (it != v.end())
  3. {
  4. if (*it % 2 == 0)//偶数
  5. {
  6. v.erase(it);
  7. }
  8. it++;
  9. }

 如果像上面这么写,也会面临一些问题

正确的写法:

  1. ldx::vector<int>::iterator it = v.begin();
  2. while (it != v.end())
  3. {
  4. if (*it % 2 == 0)//偶数
  5. {
  6. it=v.erase(it);
  7. }
  8. else
  9. {
  10. it++;
  11. }
  12. }

3.vector模拟实现总览

  1. template<class T>
  2. class vector
  3. {
  4. public:
  5. typedef T* iterator;
  6. typedef const T* const_iterator;
  7. iterator begin()
  8. {
  9. return _start;
  10. }
  11. const_iterator begin()const
  12. {
  13. return _start;
  14. }
  15. iterator end()
  16. {
  17. return _finish;
  18. }
  19. const_iterator end()const
  20. {
  21. return _finish;
  22. }
  23. //无参构造
  24. vector()
  25. :_start(nullptr)
  26. ,_finish(nullptr)
  27. ,_endOfStorage(nullptr)
  28. {}
  29. 用两个迭代区间构造
  30. template<typename inputiterator>
  31. vector(inputiterator first, inputiterator last)
  32. : _start(nullptr)
  33. , _finish(nullptr)
  34. , _endOfStorage(nullptr)
  35. {
  36. while (first != last)
  37. {
  38. push_back(*first);
  39. first++;
  40. }
  41. }
  42. void swap(vector<T>& v)
  43. {
  44. std::swap(_start, v._start);
  45. std::swap(_finish, v._finish);
  46. std::swap(_endOfStorage, v._endOfStorage);
  47. }
  48. //拷贝构造
  49. vector(const vector<T>& v)
  50. : _start(nullptr)
  51. , _finish(nullptr)
  52. , _endOfStorage(nullptr)
  53. {
  54. vector<T> temp(v.begin(),v.end());
  55. swap(temp);
  56. }
  57. ~vector()
  58. {
  59. if (_start)
  60. {
  61. delete[] _start;
  62. _start = _finish = _endOfStorage = nullptr;
  63. }
  64. }
  65. void clear()
  66. {
  67. _finish = _start;
  68. }
  69. vector<T>& operator=(vector<T> v)
  70. {
  71. swap(v);
  72. return *this;
  73. }
  74. T& operator[](size_t pos)const
  75. {
  76. assert(pos < size());
  77. return _start[pos];
  78. }
  79. T& operator[](size_t pos)
  80. {
  81. return _start[pos];
  82. }
  83. size_t capacity()
  84. {
  85. return _endOfStorage - _start;
  86. }
  87. size_t size()
  88. {
  89. return _finish - _start;
  90. }
  91. void reserve(size_t n)
  92. {
  93. size_t sz = size();
  94. if (n > capacity())
  95. {
  96. T* temp = new T[n];
  97. if (_start)
  98. {
  99. //memcpy(temp,_start,size()*sizeof(T))
  100. //如果用memcpy来将原来空间的值拷贝给新的空间,是浅拷贝,当拷贝的内容为内置类型时,不会有问题,但是当拷贝的类容是自定义类型时就会出问题
  101. for (int i = 0; i < sz; i++)
  102. {
  103. temp[i] = _start[i];
  104. }
  105. delete[] _start;
  106. }
  107. _start = temp;
  108. _finish = _start + sz;
  109. _endOfStorage = _start + n;
  110. }
  111. }
  112. void resize(size_t n, T val = T())
  113. {
  114. if (n > capacity())
  115. {
  116. reserve(n);
  117. }
  118. if (size() < n)
  119. {
  120. while (_finish < _start + n)
  121. {
  122. *_finish = val;
  123. _finish++;
  124. }
  125. }
  126. else
  127. {
  128. _finish = _start + n;
  129. }
  130. }
  131. iterator insert(iterator pos, T val)//返回指向插入元素的迭代器
  132. {
  133. int n = pos - _start;
  134. assert(pos >= _start && pos <= _finish);
  135. if (_finish == _endOfStorage)
  136. {
  137. size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
  138. reserve(newCapacity);
  139. pos = _start + n;//扩容之后需要重新调整pos的位置
  140. }
  141. iterator end = _finish;
  142. while (end != pos)
  143. {
  144. *(end) = *(end-1);
  145. end--;
  146. }
  147. *pos = val;
  148. _finish++;
  149. return pos;
  150. }
  151. void push_back(T val)
  152. {
  153. insert(_finish, val);
  154. }
  155. iterator erase(iterator pos)
  156. {
  157. assert(pos >= _start && pos < _finish);
  158. iterator temp = pos + 1;
  159. while (temp < _finish)
  160. {
  161. *(temp-1) = *temp;
  162. temp++;
  163. }
  164. _finish--;
  165. return pos;
  166. }
  167. private:
  168. iterator _start;
  169. iterator _finish;
  170. iterator _endOfStorage;
  171. };

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

闽ICP备14008679号