• 【C++学习手札】模拟实现vector


                                                          🎬慕斯主页修仙—别有洞天

                                                           ♈️今日夜电波くちなしの言葉—みゆな

                                                                    0:37━━━━━━️💟──────── 5:28
                                                                        🔄   ◀️   ⏸   ▶️    ☰ 

                                          💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


    目录

    一、vector实际的底层原理

    二、vector的模拟实现

             迭代器相关

             基本操作(迭代器失效问题)

            插入 

            删除 

            push_back()

            pop_back()

             swap()

             基本成员函数

             构造函数

            拷贝构造函数 

            析构函数 

            赋值运算符 

             空间管理

            基本状态 

             扩容操作

            resize() 

             重载[ ](最爱的运算符!!!)

     三、整体代码


    一、vector实际的底层原理

            C++中的vector底层实现是一个动态数组,也被称为可变数组。当向vector添加元素时,如果数组已经被填满,vector会自动创建一个更大的数组,将原有数据复制到新数组中,并将新元素添加到新数组中。这种自动扩容的机制使得vector能够封装任意数量的对象,而不必关心底层的数组大小。

            vector的成员变量同前面我们学的string不一样,他是通过使用指针来控制起始位置、最后一个数据位置、最大容量位置。定义如下:

    1. class vector
    2. {
    3. public:
    4. typedef T* iterator;
    5. typedef const T* const_iterator;
    6. private:
    7. iterator _start;
    8. iterator _finish;
    9. iterator _endofstorage;
    10. };

            配合图解明白: 

    二、vector的模拟实现

             迭代器相关

    1. // Vector的迭代器是一个原生指针
    2. typedef T* iterator;
    3. typedef const T* const_iterator;
    4. iterator begin()
    5. {
    6. return _start;
    7. }
    8. iterator end()
    9. {
    10. return _finish;
    11. }
    12. const_iterator begin() const
    13. {
    14. return _start;
    15. }
    16. const_iterator end() const
    17. {
    18. return _finish;
    19. }

             基本操作(迭代器失效问题)

            插入 

            在插入元素期间,可能会引起扩容,让三个指针都指向新的空间,原空间被释放,从而导致原来的迭代器指向的空间错误,对此我们可以返回新的空间的地址解决。

    1. iterator insert(iterator pos, const T& x)//迭代器失效,返回新的迭代器解决
    2. {
    3. assert(pos >= _start);
    4. assert(pos <= finish);
    5. if (_finish == _endOfStorage)
    6. {
    7. size_t len = pos - _start;//避免位置错误,因为在扩容后_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 + 1) = *end;
    15. --end;
    16. }
    17. *pos = x;
    18. ++_finish;
    19. return pos;
    20. }

            删除 

             删除由于受限制,在这里实现的时候只能通过返回指针来控制删除。通常在使用 erase 进行删除时,我们需要额外定义一个迭代器来接受原迭代器,通过选择语句来进行批量删除的判断。有如下例子:(我们要删除迭代器中可以被2整除的数,以此解决迭代器的问题)

     

    1. iterator erase(Iterator pos)//迭代器失效,返回新的迭代器解决
    2. {
    3. assert(pos >= _start);
    4. assert(pos < _finish);
    5. iterator it = pos + 1;//定义一个变量用于删除
    6. while (it < _finish)
    7. {
    8. *(it - 1) = *it;
    9. ++it;
    10. }
    11. --_finish;
    12. return pos;
    13. }

            push_back()

            复用以上insert的操作,简化代码 。

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

            pop_back()

    1. void pop_back()
    2. {
    3. assert(!empty());
    4. --_finish;
    5. }

             swap()

    1. void swap(vector& v)
    2. {
    3. std::swap(_start, v._start);
    4. std::swap(_finish, v._finish);
    5. std::swap(_endofstorage, v._endofstorage);
    6. }

             基本成员函数

            主要是复用上面的基本操作以此来简化代码。 

             构造函数

    1. vector()
    2. :_start(nullptr)
    3. ,_finish(nullptr)
    4. ,_endOfStorage(nullptr)
    5. {}

            在构造时,由于我们都要初始化。我们可以直接给成员变量在定义时就给缺省值,剩下的两个分别是根据指定数量、指定初始化值,以及根据迭代器构造。 

    1. vector()
    2. {}
    3. vector(int n, const T& value = T())
    4. {
    5. reserve(n);
    6. for (int i = 0; i < n; i++)
    7. {
    8. push_back(value);
    9. }
    10. }
    11. template<class InputIterator>
    12. vector(InputIterator first, InputIterator last)
    13. {
    14. while (first != last)
    15. {
    16. push_back(*first);
    17. ++first;
    18. }
    19. }

            拷贝构造函数 

            特别注意,在进行拷贝构造时,不要使用memcpy,在对诸如:string等类型进行拷贝时,执行的是浅拷贝。我们在这复用push_back()来进行拷贝构造。

    1. vector(const vector& v)
    2. {
    3. reserve(v.capacity());
    4. for (auto& e : v)
    5. {
    6. push_back(e);
    7. }
    8. }

            析构函数 

            需要释放在堆上动态开辟的空间,并且将指针置空,防止野指针。

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

            赋值运算符 

    1. vector& operator= (vector v)
    2. {
    3. swap(v);
    4. return *this;
    5. }

             空间管理

            基本状态 

    1. size_t capacity() const
    2. {
    3. return _endofstorage - _start;
    4. }
    5. size_t size() const
    6. {
    7. return _finish - _start;
    8. }
    9. bool empty()const
    10. {
    11. return size() == 0;
    12. }

             扩容操作

            注意这里不能使用memcpy进行对原有数据的拷贝操作,使用memcpy对于一些存储结构,如string等所做的是浅拷贝的操作。对此,使用会造成很多问题。

    1. void reserve(size_t n)
    2. {
    3. if (n > capacity())
    4. {
    5. T* tmp = new T[n];
    6. size_t sz = size();
    7. if (_start)
    8. {
    9. //memcpy(tmp, _start, sizeof(T) * sz)//这里用memcpy这里会导致string的vector出错,浅拷贝问题
    10. for (size_t i = 0; i < sz; i++)
    11. {
    12. tmp[i] = _start[i];
    13. }
    14. delete[] _start;
    15. }
    16. _start = tmp;
    17. _finish = _start + sz;
    18. _endOfStorage = _start + n;
    19. }
    20. }

            resize() 

             如果要扩的空间(n)小于当前数据个数,则截取数据。如果要扩的空间(n)大于当前数据个数则扩容。

    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. reserve(n);
    10. while (_finish < _start + n)
    11. {
    12. *_finish = val;
    13. ++_finish;
    14. }
    15. }
    16. }

             重载[ ](最爱的运算符!!!)

    1. T& operator[](size_t pos)
    2. {
    3. assert(pos < size());
    4. return _start[pos];
    5. }
    6. const T& operator[](size_t pos)const
    7. {
    8. assert(pos < size());
    9. return _start[pos];
    10. }

     三、整体代码

    1. #pragma once
    2. #define _CRT_SECURE_NO_WARNINGS 01
    3. #include
    4. using namespace std;
    5. namespace lt
    6. {
    7. template<class T>
    8. class vector
    9. {
    10. public:
    11. // Vector的迭代器是一个原生指针
    12. typedef T* iterator;
    13. typedef const T* const_iterator;
    14. iterator begin()
    15. {
    16. return _start;
    17. }
    18. iterator end()
    19. {
    20. return _finish;
    21. }
    22. const_iterator begin() const
    23. {
    24. return _start;
    25. }
    26. const_iterator end() const
    27. {
    28. return _finish;
    29. }
    30. // construct and destroy
    31. vector()
    32. :_start(nullptr)
    33. ,_finish(nullptr)
    34. ,_endOfStorage(nullptr)
    35. {}
    36. vector(int n, const T& value = T())
    37. {
    38. reserve(n);
    39. for (int i = 0; i < n; i++)
    40. {
    41. push_back(value);
    42. }
    43. }
    44. template<class InputIterator>
    45. vector(InputIterator first, InputIterator last)
    46. {
    47. while (first != last)
    48. {
    49. push_back(*first);
    50. ++first;
    51. }
    52. }
    53. vector(const vector& v)
    54. {
    55. reserve(v.capacity());
    56. for (auto& e : v)
    57. {
    58. push_back(e);
    59. }
    60. }
    61. vector& operator= (vector v)
    62. {
    63. swap(v);
    64. return *this;
    65. }
    66. ~vector()
    67. {
    68. delete[] _start;
    69. _start = _finish = _endofstorage = nullptr;
    70. }
    71. // capacity
    72. size_t capacity() const
    73. {
    74. return _endofstorage - _start;
    75. }
    76. size_t size() const
    77. {
    78. return _finish - _start;
    79. }
    80. bool empty()const
    81. {
    82. return size() == 0;
    83. }
    84. void reserve(size_t n)
    85. {
    86. if (n > capacity())
    87. {
    88. T* tmp = new T[n];
    89. size_t sz = size();
    90. if (_start)
    91. {
    92. //memcpy(tmp, _start, sizeof(T) * sz)//这里用memcpy这里会导致string的vector出错,浅拷贝问题
    93. for (size_t i = 0; i < sz; i++)
    94. {
    95. tmp[i] = _start[i];
    96. }
    97. delete[] _start;
    98. }
    99. _start = tmp;
    100. _finish = _start + sz;
    101. _endOfStorage = _start + n;
    102. }
    103. }
    104. void resize(size_t n, const T& value = T())
    105. {
    106. if (n < size())
    107. {
    108. _finish =_start + n;
    109. }
    110. else
    111. {
    112. reserve(n);
    113. while (_finish < _start + n)
    114. {
    115. *_finish = val;
    116. ++_finish;
    117. }
    118. }
    119. }
    120. ///access///
    121. T& operator[](size_t pos)
    122. {
    123. assert(pos < size());
    124. return _start[pos];
    125. }
    126. const T& operator[](size_t pos)const
    127. {
    128. assert(pos < size());
    129. return _start[pos];
    130. }
    131. ///modify/
    132. void push_back(const T& x)
    133. {/*
    134. if (_finish == _endofstorage)
    135. {
    136. reserve(capacity() == 0 ? 4 : capacity() * 2);
    137. }
    138. *_finish = x;
    139. ++_finish;*/
    140. insert(end(), x);
    141. }
    142. void pop_back()
    143. {
    144. assert(!empty());
    145. --_finish;
    146. }
    147. void swap(vector& v)
    148. {
    149. std::swap(_start, v._start);
    150. std::swap(_finish, v._finish);
    151. std::swap(_endofstorage, v._endofstorage);
    152. }
    153. iterator insert(iterator pos, const T& x)//迭代器失效,返回新的迭代器解决
    154. {
    155. assert(pos >= _start);
    156. assert(pos <= finish);
    157. if (_finish == _endOfStorage)
    158. {
    159. size_t len = pos - _start;//避免位置错误,因为在扩容后_start的地址会变化
    160. reserve(capacity() == 0 ? 4 : capacity() * 2);
    161. pos = start + len;//恢复位置
    162. }
    163. iterator end = _finish - 1;
    164. while (end >= pos)//从后往前挪
    165. {
    166. *(end + 1) = *end;
    167. --end;
    168. }
    169. *pos = x;
    170. ++_finish;
    171. }
    172. iterator erase(Iterator pos)//迭代器失效,返回新的迭代器解决
    173. {
    174. assert(pos >= _start);
    175. assert(pos < _finish);
    176. iterator it = pos + 1;//定义一个变量用于删除
    177. while (it < _finish)
    178. {
    179. *(it - 1) = *it;
    180. ++it;
    181. }
    182. --_finish;
    183. return pos;
    184. }
    185. private:
    186. iterator _start = nullptr; // 指向数据块的开始
    187. iterator _finish = nullptr; // 指向有效数据的尾
    188. iterator _endOfStorage = nullptr; // 指向存储容量的尾
    189. };
    190. }

                           感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                           

                                                                             给个三连再走嘛~  

  • 相关阅读:
    三川智控定时控制开关灯
    网络安全(黑客)自学
    如何使用CSS实现一个带有动画效果的折叠面板(Accordion)?
    FME在变更地类流向统计中的应用
    如何查询淘宝天猫的宝贝类目
    Java中StringBuffer.insert()方法具有什么功能呢?
    数据结构复杂度分析
    2022年Spring Cloud Alibaba快速上手教程
    LabVIEW玩转魔方
    Spark处理结构化数据:DataFrame、DataSet、SparkSQL
  • 原文地址:https://blog.csdn.net/weixin_64038246/article/details/134443726