C++之打造my vector篇(上)https://developer.aliyun.com/article/1624988
3.扩展延伸,深度理解代码
在VS环境下,比较严格,在迭代器方面比较严格,特别是失效迭代器的访问。
迭代器失效问题
在测试接口的过程中,有个bug就是迭代器失效问题
我们知道迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。
因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)。
1.对vector进行扩容操作,像resize,reserve等操作
还有就是在insert,push_back操作过程中涉及了扩容
#include <iostream> using namespace std; #include <vector> int main() { vector<int> v{ 1,2,3,4,5,6 }; auto it = v.begin(); // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容 // v.resize(100, 8); // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容 //量改变 v.reserve(100); // 插入元素期间,可能会引起扩容,而导致原空间被释放 v.insert(v.begin(), 0); v.push_back(8); // 给vector重新赋值,可能会引起底层容量改变 //v.assign(100, 8); /* 出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释 放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块 已经被释放的空间,而引起代码运行时崩溃。 解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给 it重新赋值即可。 */ while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; return 0; }
修改后的代码
#include <iostream> #include <vector> using namespace std; int main() { vector<int> v{ 1,2,3,4,5,6 }; // 在修改vector之后,重新获取迭代器 auto it = v.begin(); v.reserve(100); v.insert(v.begin(), 0); v.push_back(8); // 如果使用v.assign(100, 8);,也需要在之后重新获取迭代器 // 重新获取迭代器,因为之前的操作可能会改变vector的内存 it = v.begin(); while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; return 0; }
还有一点就是insert数据过后,即使没有扩容,指向容器中插入点之后的所有迭代器、指针和引用都可能失效。
所以当我们继续访问修改p的位置数据,已经失效了,需要更新失效的迭代器。
由于数据挪动,p的指向改变了,所以我们认为迭代器也失效了。
v.insert(p, 40);
p=v.insert(p, 40);
(*(p+1)) *= 10;
2.erase的删除导致的迭代器失效问题
void test_vector3() { std::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); print_container(v); // 删除所有的偶数 auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { v.erase(it); } else { ++it; } } print_container(v); } }
当我们用VS std中的接口时,会发现直接报错
所以我们也要进行重新的更新
it=v.erase(it);
#include <iostream> using namespace std; #include <vector> int main() { int a[] = { 1, 2, 3, 4 }; vector<int> v(a, a + sizeof(a) / sizeof(int)); // 使用find查找3所在位置的iterator vector<int>::iterator pos = find(v.begin(), v.end(), 3); // 删除pos位置的数据,导致pos迭代器失效。 v.erase(pos); cout << *pos << endl; // 此处会导致非法访问 return 0; }
使用memcpy的拷贝问题
当我们想拷贝几个字符串时,就会出现问题了。
问题分析:
1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。
2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
void reserve(size_t n) { if (n > capacity()) { size_t oldsize = size(); T* temp = new T[n]; memcpy(tmp, _start, old_size * sizeof(T)); /* for (size_t i = 0; i < oldsize; i++) { temp[i] = _start[i]; } */ delete[]_start; _start = temp; _finish =temp + oldsize; _end_of_storage = temp+n; } }
当我们使用这个memcpy版本进行扩容插入时,程序会出现问题
测试代码
void vector5() { vector<string> v; v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); print_container(v); v.push_back("11111111111111111111"); print_container(v); }
memcpy是浅拷贝,temp和原来的v指向了同一块空间,当调用了delete[]时,11111111...字符串被析构了,空间释放,变成随机值,后面又delete,free _start ,这时候temp指向的是释放的空间。
所以我们可以调用赋值,就可以解决问题,本质调用string的赋值,其他类型赋值一样的。
旧空间释放就不会影响新空间。
for (size_t i = 0; i < oldsize; i++) {
temp[i] = _start[i];
4.完整代码复现
#pragma once #include <iostream> #include <assert.h> #include <vector> #include <string> using namespace std; namespace my_vector { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; vector() {} vector(const vector<T>& v) { reserve(v.size()); for (auto e : v) { push_back(e); } } /* vector<T>operator=(const vector<T>& v) { if (this != v) { reserve(v.size()); for (auto e : v) { push_back(e); } } return this; } */ template <class InputIterator> vector(InputIterator first, InputIterator last) { while (first != last) { push_back(*first); first++; } } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_storage, v._end_of_storage); } // v1 = v3 //vector& operator=(vector v) vector<T>& operator=(vector<T> v) { swap(v); return *this; } void clear() { _finish = _start; } ~vector() { if (_start) { delete[]_start; _start = _finish = _end_of_storage = nullptr; } } iterator begin() { return _start; } iterator end() { return _finish; } const_iterator begin()const { return _start; } const_iterator end() const { return _finish; } void reserve(size_t n) { if (n > capacity()) { size_t oldsize = size(); T* temp = new T[n]; memcpy(temp, _start, oldsize*sizeof(T)); /* for (size_t i = 0; i < oldsize; i++) { temp[i] = _start[i]; } */ delete[]_start; _start = temp; _finish = temp + oldsize; _end_of_storage = temp + n; } } void resize(size_t n, T val = T()) { if (n < size()) { _finish = _start + n; } else { reserve(n); while (_finish < _start + n) { *_finish = val; _finish++; } } } void push_back(const T& x) { if (_finish == _end_of_storage) { reserve(capacity() == 0 ? 4 : capacity() * 2); } *_finish = x; ++_finish; } void pop_back() { assert(!empty()); --_finish; } void erase(iterator pos) { assert(pos >= _start); assert(pos <= _finish); iterator it = pos + 1; while (it != end()) { *(it - 1) = *it; it++; } _finish--; } iterator insert(iterator pos, const T& x) { assert(pos >= _start && pos <= _finish); if (_finish == _end_of_storage) { size_t len = pos - _start; reserve(capacity() == 0 ? 4 : capacity() * 2); pos = _start + len; } iterator end = _finish + 1; while (end > pos) { *end = *(end - 1); end--; } *pos = x; _finish++; return pos; } size_t size()const { return _finish - _start; } size_t capacity()const { return _end_of_storage - _start; } bool empty()const { return _start == _finish; } T& operator[](size_t i) { assert(i < size()); return _start[i]; } const T& operator[](size_t i) const { assert(i < size()); return _start[i]; } private: iterator _start = nullptr; iterator _finish = nullptr; iterator _end_of_storage = nullptr; }; template<class T> void print_vector(const vector<T>& v) { //typename vector<T>::const_iterator it = v.begin(); auto it = v.begin(); while (it != v.end()) { cout << *it << " "; it++; } cout << '\n'; for (auto e : v) { cout << e << " "; } cout << endl; } template<class Container> void print_container(const Container& v) { /* auto it = v.begin(); while (it != v.end()) { cout << *it << " "; it++; } cout << '\n'; */ for (auto e : v) { cout << e << " "; } cout << endl; } void vector1() { vector<int>v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(6); int x; cin >> x; auto p = find(v.begin(), v.end(), x); if (p != v.end()) { p = v.insert(p, 40); (*(p + 1)) *= 10; } //v.pop_back(); //v.pop_back(); //v.insert(v.begin() + 2, 5); //v.erase(v.begin() + 3); for (size_t i = 0; i < v.size(); i++) { cout << v[i] << endl; } } void vector2() { vector<int>v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(6); print_vector(v); vector<int>v1(v); vector <int>v2 = v; vector<int> v3(v1.begin(), v1.begin() + 3); print_container(v1); print_container(v2); print_container(v3); /* vector<double>v1; v1.push_back(1.1); v1.push_back(2.2); v1.push_back(3.3); v1.push_back(4.4); v1.push_back(5.5); v1.push_back(6.6); print_container(v1); */ } void vector3() { vector<int> v; v.resize(10, 1); v.reserve(20); print_container(v); cout << v.size() << endl; cout << v.capacity() << endl; v.resize(15, 2); print_container(v); v.resize(25, 3); print_container(v); v.resize(5); print_container(v); } void vector4() { vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(4); v.push_back(5); print_container(v); // 删除所有的偶数 auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { v.erase(it); } else {//不加else,不会删除连续的偶数,会++两次 ++it; } } print_container(v); } void test_vector3() { std::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); print_container(v); // 删除所有的偶数 auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { it=v.erase(it); } else { ++it; } } print_container(v); } void vector5() { vector<string> v; v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); print_container(v); v.push_back("11111111111111111111"); print_container(v); } }
结束语
本期博客就到此结束啦,相信通过自己对vector的实现,大家对vector有了更深的了解。
最后希望友友们给小编点点赞吧,感谢各位友友的支持!!!