• 【C++】模拟实现STL容器:vector



    需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


     目录

    一、vector迭代器失效问题

    1、Visual Studio和g++对迭代器失效问题的表现

    2、解决迭代器失效的方法

    二、模拟实现构造函数调用不明确

    1、问题描述

    2、解决调用不明确的方法

    三、reserve中的深浅拷贝问题

    1、reserve中浅拷贝发生原因

    2、浅拷贝发生的图解

    3、解决方法

    四、模拟实现vector整体代码


    一、vector迭代器失效问题

    1、Visual Studio和g++对迭代器失效问题的表现

    在Visual Studio2022中,调用vector的insert()和erase()接口后,it迭代器(包括it之后的自定义迭代器)将会失效,如果仍操作这些已经失效的迭代器,编译器将会引发异常。

    博主尝试在Linux的g++编译器(4.8.5版本)下运行相同debug版本的程序(编译时带上-g选项),发现g++中调用完insert()和erase()接口后,it迭代器并未失效,甚至可以操纵it读写_end_of_storage-_finish这部分空间,这是不安全的。

    所以,后续调用完insert()和erase()接口后,我们一律认为当前迭代器失效!

    2、解决迭代器失效的方法

    使用时让当前迭代器接收insert()和erase()的返回值,更新迭代器即可。

    二、模拟实现构造函数调用不明确

    1、问题描述

    1. vector(size_t n, const T& val = T())//这里的形参用size_t就会引发这两个构造函数调用问题
    2. :_start(nullptr)
    3. , _finish(nullptr)
    4. , _end_of_storage(nullptr)
    5. {
    6. reserve(n);
    7. for (size_t i = 0; i < n; ++i)
    8. {
    9. push_back(val);
    10. }
    11. }
    12. template <class InputIterator>
    13. vector(InputIterator first, InputIterator last)
    14. :_start(nullptr)
    15. , _finish(nullptr)
    16. , _end_of_storage(nullptr)
    17. {
    18. while (first != last)
    19. {
    20. push_back(*first++);
    21. }
    22. }

    本意是想使用第一种构造方式,用3个6进行构造。编译器会根据形参调用最匹配的函数重载。

    第一个构造函数的第一个形参是size_t,形参去匹配的话需要发生隐式类型转换。

    但是这两个参数更匹配第二个构造函数(因为第二个模板可以为int,完全匹配),一旦走第二个构造函数,该构造函数内部是要对first进行解引用操作,所以编译器会报非法的间接寻址(解引用)错误。

    2、解决调用不明确的方法

    针对构造函数vector(size_t n, const T& val = T()),我们多重载一个vector(int n, const T& val = T())版本的构造函数即可解决该问题。

    三、reserve中的深浅拷贝问题

    1、reserve中浅拷贝发生原因

    这句memcpy表面上把原来的数据全部拷贝到tmp里面了,但是,这只是按字节的拷贝,如果当前类型为vector>等涉及深浅拷贝类型,将会发生浅拷贝

    2、浅拷贝发生的图解

    memcpy会把vector>,从_start位置开始,按字节进行拷贝。如图所示,拷贝的对象是4个vector,这是一种浅拷贝!

    3、解决方法

    1. void reserve(size_t n)
    2. {
    3. //扩容
    4. if (n > capacity())
    5. {
    6. size_t oldSize = size();//先记录下size,后续解决finish失效
    7. T* tmp = new T[n];
    8. if (_start != nullptr)
    9. {
    10. //memcpy(tmp, _start, sizeof(T) * oldSize);//浅拷贝
    11. for (size_t i = 0; i < oldSize; ++i)
    12. {
    13. tmp[i] = _start[i];//调用赋值运算符重载完成深拷贝
    14. }
    15. delete[] _start;
    16. }
    17. _start = tmp;
    18. _finish = _start + oldSize;//异地扩容后_finish失效,需重新设定_finish
    19. _end_of_storage = _start + n;
    20. }
    21. }

    借助赋值运算符重载,完成深拷贝。

    四、模拟实现vector整体代码

    1. #pragma once
    2. #include
    3. #include
    4. using std::cout;
    5. using std::cin;
    6. using std::endl;
    7. namespace jly
    8. {
    9. template <class T>
    10. class vector
    11. {
    12. public:
    13. //构造函数
    14. vector()
    15. :_start(nullptr)
    16. , _finish(nullptr)
    17. , _end_of_storage(nullptr)
    18. {}
    19. vector(size_t n, const T& val = T())
    20. :_start(nullptr)
    21. , _finish(nullptr)
    22. , _end_of_storage(nullptr)
    23. {
    24. reserve(n);
    25. for (size_t i = 0; i < n; ++i)
    26. {
    27. push_back(val);
    28. }
    29. }
    30. vector(int n, const T& val = T())//多重载一个int版本的构造,解决调函数不明确的问题
    31. :_start(nullptr)
    32. , _finish(nullptr)
    33. , _end_of_storage(nullptr)
    34. {
    35. reserve(n);
    36. for (int i = 0; i < n; ++i)
    37. {
    38. push_back(val);
    39. }
    40. }
    41. template <class InputIterator>
    42. vector(InputIterator first, InputIterator last)
    43. :_start(nullptr)
    44. , _finish(nullptr)
    45. , _end_of_storage(nullptr)
    46. {
    47. while (first != last)
    48. {
    49. push_back(*first++);
    50. }
    51. }
    52. //拷贝构造
    53. void swap(vector& v)//一定要加引用,不然拷贝构造函数调用swap会引发无限拷贝
    54. {
    55. std::swap(_start, v._start);
    56. std::swap(_finish, v._finish);
    57. std::swap(_end_of_storage, v._end_of_storage);
    58. }
    59. vector(const vector& v)
    60. : _start(nullptr)
    61. , _finish(nullptr)
    62. , _end_of_storage(nullptr)
    63. {
    64. vector tmp(v.begin(), v.end());
    65. swap(tmp);
    66. }
    67. //vector(const vector& v)//写法二
    68. // : _start(nullptr)
    69. // , _finish(nullptr)
    70. // , _end_of_storage(nullptr)
    71. //{
    72. // reserve(v.capacity());
    73. // for (const auto& e : v)
    74. // {
    75. // push_back(e);
    76. // }
    77. //}
    78. //赋值运算符重载
    79. vector& operator=(vector v)//能解决自己给自己赋值的问题
    80. {
    81. swap(v);
    82. return *this;
    83. }
    84. //析构函数
    85. ~vector()
    86. {
    87. delete[] _start;
    88. _start = _finish = _end_of_storage = nullptr;
    89. }
    90. //迭代器
    91. typedef T* iterator;
    92. iterator begin()
    93. {
    94. return _start;
    95. }
    96. iterator end()
    97. {
    98. return _finish;
    99. }
    100. const iterator begin()const
    101. {
    102. return _start;
    103. }
    104. const iterator end()const
    105. {
    106. return _finish;
    107. }
    108. T& operator[](size_t pos)
    109. {
    110. return _start[pos];
    111. }
    112. const T& operator[](size_t pos)const
    113. {
    114. return _start[pos];
    115. }
    116. //reserve接口
    117. void reserve(size_t n)
    118. {
    119. //扩容
    120. if (n > capacity())
    121. {
    122. size_t oldSize = size();//先记录下size,后续解决finish失效
    123. T* tmp = new T[n];
    124. if (_start != nullptr)
    125. {
    126. //memcpy(tmp, _start, sizeof(T) * oldSize);//浅拷贝
    127. for (size_t i = 0; i < oldSize; ++i)
    128. {
    129. tmp[i] = _start[i];//调用赋值运算符重载完成深拷贝
    130. }
    131. delete[] _start;
    132. }
    133. _start = tmp;
    134. _finish = _start + oldSize;//异地扩容后_finish失效,需重新设定_finish
    135. _end_of_storage = _start + n;
    136. }
    137. }
    138. //resize接口
    139. void resize(size_t n, T val = T())//val给T类型的缺省值
    140. {
    141. if (n > capacity())//n大于capacity,要扩容
    142. {
    143. reserve(n);
    144. while (_finish < _start + n)
    145. {
    146. *_finish = val;
    147. ++_finish;
    148. }
    149. }
    150. else if (n > size())//n小于capacity但大于原size
    151. {
    152. while (_finish < _start + n)
    153. {
    154. *_finish = val;
    155. ++_finish;
    156. }
    157. }
    158. else//缩size的情况
    159. {
    160. _finish = _start + n;
    161. }
    162. }
    163. //push_back/pop_back接口
    164. void push_back(const T& val)
    165. {
    166. if (_finish == _end_of_storage)
    167. {
    168. size_t newCapacity = capacity() == 0 ? 4 : 2 * capacity();
    169. reserve(newCapacity);
    170. }
    171. *_finish = val;
    172. ++_finish;
    173. }
    174. void pop_back()
    175. {
    176. assert(!empty());
    177. --_finish;
    178. }
    179. //insert和erase接口
    180. iterator insert(iterator pos, const T& val)
    181. {
    182. assert(pos >= _start && pos <= _finish);
    183. //判断扩容
    184. if (_finish == _end_of_storage)
    185. {
    186. size_t len = pos - _start;//需要处理pos迭代器失效问题,记录len
    187. size_t newCapacity = capacity() == 0 ? 4 : 2 * capacity();
    188. reserve(newCapacity);//扩容后pos迭代器失效
    189. pos = _start + len;//重新设定pos
    190. }
    191. //挪动数据
    192. for (iterator i = _finish; i > pos; --i)
    193. {
    194. *i = *(i - 1);
    195. }
    196. //填入数据
    197. *pos = val;
    198. ++_finish;
    199. return pos;
    200. }
    201. iterator erase(iterator pos)
    202. {
    203. assert(pos >= _start && pos < _finish);
    204. for (iterator i = pos; i < _finish - 1; ++i)
    205. {
    206. *i = *(i + 1);
    207. }
    208. --_finish;
    209. return pos;
    210. }
    211. //小接口
    212. size_t size()const
    213. {
    214. return _finish - _start;
    215. }
    216. size_t capacity()const
    217. {
    218. return _end_of_storage - _start;
    219. }
    220. bool empty()const
    221. {
    222. return _start == _finish;
    223. }
    224. void clear()
    225. {
    226. _finish = _start;
    227. }
    228. private:
    229. iterator _start;
    230. iterator _finish;
    231. iterator _end_of_storage;
    232. };
    233. /测试部分
    234. void test()
    235. {
    236. vector<int> v(1, 5);
    237. vector<int> v1(v);
    238. for (auto e : v1)
    239. {
    240. cout << e << " ";
    241. }
    242. }
    243. class Solution {
    244. public:
    245. vectorint>> generate(int numRows) {
    246. vectorint>> vv;
    247. vv.resize(numRows);
    248. for (size_t i = 0; i < vv.size(); ++i)
    249. {
    250. vv[i].resize(i + 1, 0);
    251. vv[i][0] = vv[i][vv[i].size() - 1] = 1;
    252. }
    253. for (size_t i = 0; i < vv.size(); ++i)
    254. {
    255. for (size_t j = 0; j < vv[i].size(); ++j)
    256. {
    257. if (vv[i][j] == 0)
    258. {
    259. vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
    260. }
    261. }
    262. }
    263. for (size_t i = 0; i < vv.size(); ++i)
    264. {
    265. for (size_t j = 0; j < vv[i].size(); ++j)
    266. {
    267. cout << vv[i][j] << " ";
    268. }
    269. cout << endl;
    270. }
    271. return vv;
    272. }
    273. };
    274. void test_vector()
    275. {
    276. vectorint>> vv;
    277. vector<int> v(5, 1);
    278. vv.push_back(v);
    279. vv.push_back(v);
    280. vv.push_back(v);
    281. vv.push_back(v);
    282. vv.push_back(v);
    283. for (size_t i = 0; i < vv.size(); ++i)
    284. {
    285. for (size_t j = 0; j < vv[i].size(); ++j)
    286. {
    287. cout << vv[i][j] << " ";
    288. }
    289. cout << endl;
    290. }
    291. cout << endl;
    292. }
    293. }
  • 相关阅读:
    python dicttoxml模块简介
    opencv 的应用(1)
    我的创作纪念日
    trivy 获取基础镜像源码分析
    视频倒放怎么制作?快来学会这几个简单的方法
    Cesium加载离线地图和离线地形
    LVS+keepalive配置DNS的UDP53端口负载均衡
    # 智慧社区管理系统-核心业务管理-03投诉信息
    设计模式之兼容不同厂家的相机
    面向对象编程——类与对象(C#)(未写完)
  • 原文地址:https://blog.csdn.net/gfdxx/article/details/128010148