目录
1.1 std::vector的核心框架接口的模拟实现bit::vector
后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教! ——By 作者:新晓·故知
1.vector深度剖析及模拟实现:
vector的模拟实现主要对常用的函数接口:构造函数、拷贝构造函数、析构函数、赋值重载函数、vector迭代器、reserve、push_back、pop_back、insert、resize、erase等。
对vector在Linux环境下g++编译器与Windows环境下VS系列编译器的vector迭代器失效问题进行对比、解决。
1.1 std::vector的核心框架接口的模拟实现bit::vector
#include <iostream> using namespace std; #include <assert.h> //自定义命名空间,防止与库冲突 namespace bit { template<class T> class vector { public: // Vector的迭代器是一个原生指针 typedef T* iterator; typedef const T* const_iterator; iterator begin() { return _start; } iterator end() { return _finish; } const_iterator cbegin() const { return _start; } const_iterator cend() const { return _finish; } // construct and destroy vector() : _start(nullptr) , _finish(nullptr) , _endOfStorage(nullptr) {} vector(int n, const T& value = T()) : _start(nullptr) , _finish(nullptr) , _endOfStorage(nullptr) { reserve(n); while (n--) { push_back(value); } } // 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器 // 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器 template<class InputIterator> vector(InputIterator first, InputIterator last) { reserve(last - first); while (first != last) { push_back(*first); ++first; } } vector(const vector<T>& v) : _start(nullptr) , _finish(nullptr) , _endOfStorage(nullptr) { reserve(v.capacity()); iterator it = begin(); const_iterator vit = v.cbegin(); while (vit != v.cend()) { *it++ = *vit++; } } vector<T>& operator=(vector<T> v) { swap(v); return *this; } ~vector() { delete[] _start; _start = _finish = _endOfStorage = nullptr; } // capacity size_t size() const { return _finish - _start; } size_t capacity() const { return _endOfStorage - _start; } bool empty() const { return _first == _finish; } void reserve(size_t n) { if (n > capacity()) { size_t oldSize = size(); T* tmp = new T[n]; // 这里直接使用memcpy? //if (_start) // memcpy(tmp, _start, sizeof(T)*size); if (_start) { for (size_t i = 0; i < oldSize; ++i) tmp[i] = _start[i]; } _start = tmp; _finish = _start + oldSize; _endOfStorage = _start + n; } } void resize(size_t n, const T& value = T()) { // 1.如果n小于当前的size,则数据个数缩小到n if (n <= size()) { _finish = _start + n; return; } // 2.空间不够则增容 if (n > capacity()) reserve(n); // 3.将size扩大到n iterator it = _finish; _finish = _start + n; while (it != _finish) { *it = value; ++it; } } ///access/// T& operator[](size_t pos) { return _start[pos]; } const T& operator[](size_t pos)const { return _start[pos]; } ///modify/ void push_back(const T& x) { insert(end(), x); } void pop_back() { erase(--end()); } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_endOfStorage, v._endOfStorage); } iterator insert(iterator pos, const T& x) { assert(pos <= _finish); // 空间不够先进行增容 if (_finish == _endOfStorage) { //size_t size = size(); size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2; reserve(newCapacity); // 如果发生了增容,需要重置pos pos = _start + size(); } iterator end = _finish - 1; while (end >= pos) { *(end + 1) = *end; --end; } *pos = x; ++_finish; return pos; } // 返回删除数据的下一个数据 // 方便解决:一边遍历一边删除的迭代器失效问题 iterator erase(iterator pos) { // 挪动数据进行删除 iterator begin = pos + 1; while (begin != _finish) { *(begin - 1) = *begin; ++begin; } --_finish; return pos; } private: iterator _start; // 指向数据块的开始 iterator _finish; // 指向有效数据的尾 iterator _endOfStorage; // 指向存储容量的尾 }; }
1.2 使用memcpy拷贝问题
假设模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝,以下代码会发生什么问题?int main() { bite::vector<bite::string> v; v.push_back("1111"); v.push_back("2222"); v.push_back("3333"); return 0; }
问题分析:1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中2. 如果拷贝的是自定义类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。
1.3 对bit::vector核心接口的测试
// constructing vectors void TestVector1() { // constructors used in the same order as described above: bite::vector<int> first; // empty vector of ints bite::vector<int> second(4, 100); // four ints with value 100 bite::vector<int> third(second.Begin(), second.End()); // iterating through second bite::vector<int> fourth(third); // a copy of third // the iterator constructor can also be used to construct from arrays: int myints[] = { 16, 2, 77, 29 }; bit::vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int)); std::cout << "The contents of fifth are:"; for (bit::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it) std::cout << *it << " "; std::cout << endl; // 测试T是string时,拷贝问题 bit::vector<string> strV; strV.PushBack("1111"); strV.PushBack("2222"); strV.PushBack("3333"); strV.PushBack("4444"); for (size_t i = 0; i < strV.size(); ++i) { cout << strV[i] << " "; } cout << endl; } //vector iterator的使用 void PrintVector(const bite::vector<int>& v) { // 使用const迭代器进行遍历打印 bit::vector<int>::const_iterator it = v.begin(); while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; } void TestVector2() { // 使用push_back插入4个数据 bite::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); PrintVector(v); // 使用迭代器进行修改 auto it = v.begin(); while (it != v.end()) { *it *= 2; ++it; } PrintVector(v); // 这里可以看出C++11支持iterator及接口,就支持范围for for (auto e : v) cout << e << " "; } // find / insert / erase void TestVector3() { int a[] = { 1, 2, 3, 4 }; bite::vector<int> v(a, a + sizeof(a) / sizeof(a[0])); // 使用find查找3所在位置的iterator auto pos = find(v.begin(), v.end(), 3); // 在pos位置之前插入30 v.insert(pos, 30); PrintVector(v); // 删除pos位置的数据 pos = find(v.begin(), v.end(), 3); v.Erase(pos); PrintVector(v); } // iterator失效问题 void TestVector4() { int a[] = { 1, 2, 3, 4 }; bite::vector<int> v(a, a + sizeof(a) / sizeof(a[0])); // 删除pos位置的数据,导致pos迭代器失效 auto pos = find(v.begin(), v.end(), 3); v.erase(pos); cout << *pos << endl; // 此处会导致非法访问 // 在pos位置插入数据,导致pos迭代器失效。 // insert会导致迭代器失效,是因为insert可 // 能会导致增容,增容后pos还指向原来的空间,而原来的空间已经释放了。 pos = find(v.begin(), v.end(), 3); v.insert(pos, 30); cout << *pos << endl; // 此处会导致非法访问 // 实现删除v中的所有偶数 // 下面的程序会崩溃掉,如果是偶数,erase导致it失效 // 对失效的迭代器进行++it,会导致程序崩溃 auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) v.erase(it); ++it; } // 以上程序要改成下面这样,erase会返回删除位置的下一个位置 it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) it = v.erase(it); else ++it; } }
注:一般vector删除数据,都不考虑缩容的方案。
缩容方案:size()<capacity()/2时,可以考虑开辟一个size()大小的空间,拷贝数据,释放旧空间。缩容方案本质是时间换空间。一般设计都不考虑缩容,因为实际比较关注时间效率,不关注空间效率,因为现在硬件设备空间都比较大,空间存储也比较便宜。
string迭代器也有失效问题,但string不常使用迭代器,而多使用下标+[ ],vector多使用迭代器,要注意失效问题。
1.4动态二维数组理解
// 以杨辉三角的前n行为例:假设n为5 void test5(size_t n) { // 使用vector定义二维数组vv,vv中的每个元素都是vector<int> bit::vector<bit::vector<int>> vv(n); // 将二维数组每一行中的vecotr<int>中的元素全部设置为1 for (size_t i = 0; i < n; ++i) vv[i].resize(i + 1, 1); // 给杨辉三角出第一列和对角线的所有元素赋值 for (int i = 2; i < n; ++i) { for (int j = 1; j < i; ++j) { vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1]; } } }
bit::vector<bit::vector<int>> vv(n); 构造一个vv动态二维数组,vv中总共有n个元素,每个元素都是vector类型的,每行没有包含任何元素,如果n为5时如下所示:
2.vector模拟实现源码:
2.1:完整源码:
(1)vector.h:
#pragma once //模拟实现vector #include<iostream> #include<string> #include<vector> #include<cassert> using namespace std; //自定义命名空间,防止与库里的冲突 namespace my { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; //构造函数 vector() :_start(nullptr) ,_finish(nullptr) ,_endofstorage(nullptr) {} //使用n个val构造函数 vector(size_t n, const T& val = T()) :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { reserve(n); for (size_t i = 0; i < n; ++i) { push_back(val); } } vector(int n, const T& val = T()) :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { reserve(n); for (size_t i = 0; i < n; ++i) { push_back(val); } } //类模板,类构造 template<class InputIterator> vector(InputIterator first, InputIterator last) : _start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { while (first != last) { push_back(*first); ++first; } } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_endofstorage, v._endofstorage); } //拷贝构造函数(使用现代写法) vector(const vector<T>& v) : _start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { vector<T> tmp(v.begin(), v.end()); swap(tmp); //this->swap(tmp); } //赋值重载函数(现代写法) vector<T>& operator=(vector<T> v) { swap(v); //this->swap(v); return *this; } //析构函数(资源管理) ~vector() { if (_start) { delete[] _start; _start = _finish = _endofstorage = nullptr; } } iterator begin() { return _start; } iterator end() { return _finish; } const_iterator begin() const { return _start; } const_iterator end() const { return _finish; } size_t size() const { return _finish - _start; } size_t capacity() const { return _endofstorage - _start; } void reserve(size_t n) { size_t sz = size(); if (n > capacity()) { T* tmp = new T[n]; if (_start) { //memcpy(tmp, _start, size() * sizeof(T)); //浅拷贝问题 for (size_t i = 0;i < size(); ++i) { tmp[i] = _start[i]; } delete[] _start; } _start = tmp; } _finish = _start + sz; _endofstorage = _start + n; } //总结:vector<T>中,当T涉及深拷贝的类型时,如:string、vector<T>等等, //扩容使用memcpy拷贝数据会存在浅拷贝问题,造成析构两次 //解决:使用传值拷贝,如上 void push_back(const T& x) { /*if (_finish == _endofstorage) { size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newCapacity); } *_finish = x; ++_finish;*/ //附用insert insert(end(),x); } void pop_back() { /*if (_finish > _start) { --_finish; }*/ //附用erase erase(end()-1); } T& operator[](size_t pos) { assert(pos < size()); return _start[pos]; } const T& operator[](size_t pos) const { assert(pos < size()); return _start[pos]; } //写在类里的小函数,被当做内联函数处理,减少了多次调用建立栈帧 //void resize(size_t n, const T& val = T()) void resize(size_t n, T val = T()) //T()匿名对象,调用默认构造函数,若T()为int(内置类型), { //C++对于内置类型也可以认为有构造函数、析构函数,才能支持模板,只是int为0,double为0.1 /*int i = 0; int j = int(); int k = int(1);*/ if (n > capacity()) { reserve(n); } if (n > size()) { while (_finish < _start + n) { *_finish = val; ++_finish; } } else { _finish = _start + n; } } iterator insert(iterator pos, const T& x) //返回值为iterator,解决迭代器失效问题 { //如果pos传引用,但有些时候,传不过去 //检查参数 assert(pos >= _start && pos <= _finish); //扩容 //扩容以后,pos就失效了,要更新 //insert导致的迭代器失效,是因为pos没更新 if (_finish == _endofstorage) { size_t n = pos - _start; size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newCapacity); pos = _start + n; } //挪动数据 iterator end = _finish - 1; while (end >= pos) { *(end + 1) = *end; --end; } *pos = x; ++_finish; return pos; } //总结:迭代器在insert里的两种失效: //1.pos失效 2.迭代器it失效 iterator erase(iterator pos) { assert(pos >= _start && pos <= _finish); iterator it = pos + 1; while (it != _finish) { *(it - 1) = *it; ++it; } --_finish; return pos; } //总结: //1.erase的失效都是意义变了,过着不在有效访问数据有效范围 //2.一般不会使用缩容的方案,那么erase的失效也不存在野指针的失效 //在VS和Linux不同环境下,处理不同 //erase(pos),使得pos失效,pos的意义改变,但是不同环境平台的处理不一样 //在使用的时候,统一以失效的角度看待 //整体总结: //对于insert和erase造成的迭代器失效问题,Linux环境下g++检查不严格,基本依靠操作系统自身野指针越界检查机制 //Windows环境下,VS系列检查严格,使用一些强制检查机制,意义改变造成的失效,也可能会检查出来 //vector迭代器的失效有两种: //1.扩容、缩容,导致形成野指针失效 //2.迭代器指向的位置意义改变 //这些通过操作系统的越界检查机制不一定能检查到 //而通过编译器实现机制检查,相对靠谱 void clear() { _finish = _start; } private: iterator _start; iterator _finish; iterator _endofstorage; }; }
(2)test.cpp:
#include "vector.h" void TestVector1() { my::vector<int> v; //注意类域,这里使用模拟的vector,要加上类名 v.push_back(2); v.push_back(0); v.push_back(2); v.push_back(2); v.push_back(8); 1.使用范围for遍历 //for (auto e : v) //{ // cout << e << " "; //} //cout << endl; //2.使模拟迭代器遍历 my::vector<int>::iterator it = v.begin(); while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; } void TestVector2() { my::vector<int> v; v.push_back(2); v.push_back(0); v.push_back(2); v.push_back(2); v.push_back(8); //模拟迭代器遍历 my::vector<int>::iterator it = v.begin(); while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; v.pop_back(); v.pop_back(); v.pop_back(); //1.下标+[] 进行遍历 有3种遍历方式 for (size_t i = 0; i < v.size(); ++i) { cout << v[i] << " "; } cout << endl; } void TestVector3() { my::vector<int> v; v.resize(6, 6); for (auto e : v) { cout << e << " "; } cout << endl; } void TestVector4() { my::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); //v.push_back(5); //模拟迭代器遍历 my::vector<int>::iterator it = v.begin(); while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; v.insert(v.begin(), 0); //v.insert(v.begin()+2, 0); for (auto e : v) { cout << e << " "; } cout << endl; } void TestVector5() { //在所有的偶数前面插入9 my::vector<int> v; //v.reserve(10); v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); for (auto e : v) { cout << e << " "; } cout << endl; my::vector<int>::iterator it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { it=v.insert(it, 9); //insert扩容以后,it就失效了,it值传递给pos,pos改变不影响it //it指向的位置变了 ++it; } ++it; } for (auto e : v) { cout << e << " "; } cout << endl; } void TestVector6() { my::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); for (auto e : v) { cout << e << " "; } cout << endl; cout << "v.size():" << v.size()<<" " << "v.capacity:" << v.capacity() << endl; auto pos = find(v.begin(), v.end(), 2); if (pos != v.end()) { v.erase(pos); } for (auto e : v) { cout << e << " "; } cout << endl; cout << "v.size():" << v.size() << " " << "v.capacity:" << v.capacity() << endl; cout << *pos << endl; *pos = 10; for (auto e : v) { cout << e << " "; } } void TestVector7() { //删除所有的偶数 my::vector<int> v; //std::vector<int> v; //库里的vector v.push_back(2); v.push_back(1); v.push_back(2); v.push_back(2); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(6); v.push_back(6); v.push_back(6); v.push_back(6); v.push_back(5); for (auto e : v) { cout << e << " "; } cout << endl; auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { it=v.erase(it); //处理迭代器失效问题的解决方式 } else { ++it; } } for (auto e : v) { cout << e << " "; } cout << endl; } void TestVector8() { my::vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); for (auto e : v1) { cout << e << " "; } cout << endl; my::vector<int> v2(v1.begin(), v1.end()); std::string s("hello"); my::vector<int> v3(s.begin(), s.end()); for (auto e : v2) { cout << e << " "; } cout << endl; for (auto e : v3) { cout << e << " "; } cout << endl; } void TestVector9() { my::vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); for (auto e : v1) { cout << e << " "; } cout << endl; my::vector<int> v2(v1); //拷贝构造 for (auto e : v2) { cout << e << " "; } cout << endl; my::vector<int> v3; v3 = v1; //赋值 for (auto e : v3) { cout << e << " "; } cout << endl; } void TestVector10() { //使用n个val构造函数 my::vector<int> v1(10,2); for (auto e : v1) { cout << e << " "; } cout << endl; my::vector<char> v2(10, 'q'); for (auto e : v2) { cout << e << " "; } cout << endl; } void TestVector11() { //杨辉三角 class Solution { public: my::vector<my::vector<int>> generate(int numRows) { my::vector<my::vector<int>> vv; vv.resize(numRows); for (size_t i = 0; i < vv.size(); ++i) { //每次每行个数递增1 vv[i].resize(i + 1, 0); vv[i][0] = 1; vv[i][vv[i].size() - 1] = 1; } for (int i = 0; i < vv.size(); ++i) { for (int j = 0; j < vv[i].size(); ++j) { if (vv[i][j] == 0) { //中间位置等于上一行j-1列和j列相加 vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j]; } cout << vv[i][j] << " "; } cout << endl; } cout << endl; return vv; } }; my::vector<my::vector<int>> vv = Solution().generate(7); for (int i = 0; i < vv.size(); ++i) { for (int j = 0; j < vv[i].size(); ++j) { cout << vv[i][j] << " "; } cout << endl; } } int main() { //TestVector1(); //TestVector2(); //TestVector3(); //TestVector4(); //TestVector5(); //TestVector6(); //TestVector7(); //TestVector8(); //TestVector9(); //TestVector10(); TestVector11(); return 0; }
2.2例题:使用vector实现杨辉三角:
后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!
——By 作者:新晓·故知