目录
vector的基本使用
构造函数:
拷贝构造:
迭代器:
容器:
元素访问:
修改:
emplace_back和push_back区别
结构化绑定
迭代器失效
vector的模拟
迭代器:
容器:
元素访问:
元素修改:
构造、赋值和析构:
总代码综合:
总结:
vector的基本使用
构造函数:
template<class Constain> void Print(const Constain& con) { for (auto& e : con) { cout << e << ' '; } cout << endl; } int main() { //以下三种都可以一般会忽略括号,直接用花括号 //vector<int> v1({ 1,2,3,4,5 }); vector<int> v1{ 1,2,3,4,5 }; //vector<int> v1 = { 1,2,3,4,5 }; Print(v1); vector<int> v2(20, 100); Print(v2); vector<int> v3(v1.begin() + 2, v1.end()); Print(v3); return 0; }运行结果:
以上是我觉得较为有用的初始化构造函数(c++98)需要其他构造可以直接查阅文档vector::vector - C++ Reference
拷贝构造:
vector<int> v4 = v1;//这里继承了上面的代码,{1,2,3,4,5} Print(v4);迭代器:
这里迭代器就不一一写了,这里可以理解成像指针一样的东西,进行遍历使用就好了,如:
//可修改 void test02() { vector<int> v1 = {1,2,3,4,5}; vector<int>::iterator it1 = v1.begin(); while (it1 != v1.end()) { cout << *it1 << ' '; ++it1; } } //只读 void test03() { vector<int> v1 = {1,2,3,4,5}; vector<int>::const_iterator it1 = v1.cbegin(); while (it1 != v1.cend()) { cout << *it1 << ' '; ++it1; } } //反向迭代器 void test04() { vector<int> v1 = {1,2,3,4,5}; vector<int>::reverse_iterator it1 = v1.rbegin(); while (it1 != v1.rend()) { cout << *it1 << ' '; ++it1; } }运行结果:
容器:
void test05() { vector<string> v1 = { "hellow","world","!!!!!" }; cout << "大小:" << v1.size() << endl; cout << "扩容前:" << v1.capacity() << endl; cout << v1.max_size() << endl;//没实际意义(扩容阔不了这么大) v1.resize(5, "bear"); Print(v1); cout << "resize扩容后:" << v1.capacity() << endl; cout << "判空:" << v1.empty() << endl; v1.reserve(10); cout <<"reserve扩容后:" << v1.capacity() << endl; v1.shrink_to_fit(); cout << "shrink_to_fit缩容后:" << v1.capacity() << endl; }元素访问:
void test06() { vector<string> v1 = { "hellow","world","!!!!!" }; cout << "operator[]:" << v1[1] << endl; cout <<"at:" << v1.at(1) << endl; cout << "front:" << v1.front() << endl; cout << "back:" << v1.back() << endl; cout << "data:" << v1.data() << endl;//返回起始元素地址 }运行结果:
修改:
void test07() { vector<int> v1{1,2,3,4,5}; v1.assign(20, 100);//替代原来的vector Print(v1); vector<int> v2; v2.push_back(1); v2.push_back(2); v2.push_back(3); v2.push_back(4); v2.push_back(5); Print(v2); v2.pop_back(); cout << "pop_back后:"; Print(v2); v2.insert(v2.begin()+2, 100); cout << "insert插入后:"; Print(v2); v2.erase(v2.begin() + 1); cout << "erase删除后:"; Print(v2); vector<int> v3; v3.swap(v2); cout << "交换后的v2:"; Print(v2); cout << "交换后的v3:"; Print(v3); v3.clear(); cout << "clear的v3:"; Print(v3); }emplace_back和push_back区别
emplace_back和push_back(构造一个临时对象,然后将其拷贝或移动到容器中)在初始化时,在特殊场景下emplace_back效率会更高(直接在容器内存中构造对象,避免创建临时对象)push_back和emplace_back在扩容过程中都会调用拷贝构造。
void test08() { struct A { A(int a,int b,int c) :_a(a) ,_b(b) ,_c(c) { cout << "A()" << endl; } A(const A& aa) { cout << "A(const A& aa)" << endl; _a = aa._a; _b = aa._b; _c = aa._c; } int _a = 1; int _b = 2; int _c = 3; }; vector<int> v1; //内置类型效率差不多 v1.push_back(1); v1.emplace_back(1); for (auto& e : v1) { cout << e << " "; } cout << endl; cout << endl; vector<A> v2; //v2.reserve(10); //完整对象 A aa1(4, 5, 6); cout << "********************************" << endl; cout << "push_back完整实例化对象:"<<endl; v2.push_back(aa1); v2.push_back(A(7,8,9));//匿名对象 cout << "push_back:传递构造参数(不是完整实例化对象)" << endl; //需要调用构造,再调用拷贝构造 v2.push_back({ 1,2,3 }); cout << "emplace_back完整实例化对象:"<<endl;; //这里拷贝2次是因为第一个拷贝是因为扩容拷贝,然后第二次是需要拷贝到vector中 v2.emplace_back(aa1); v2.emplace_back(A(7, 8, 9)); cout << "emplace_back:传递构造参数(不是完整实例化对象)" << endl; //避免临时对象的产生,直接调用构造(效率较高) v2.emplace_back( 1,2,3 ); cout << endl; cout << endl; }这里很明显在传递构造参数时,就会使得拷贝构造变少(甚至不拷贝),但是在其他方面如已初始化的反而会拷贝更多拷贝;
结构化绑定
结构化绑定(用于类型是自定义类型的类,就是一个自定义类型有多个成员访问就好用,和范围for一样,只不过范围for不好写多个成员访问,这里结构化绑定就是可以直接访问)。可能发生拷贝构造,可以用引用消除这个拷贝构造
void test09() { struct A { A(int a, int b, int c) :_a(a) , _b(b) , _c(c) { cout << "A()" << endl; } A(const A& aa) { cout << "A(const A& aa)" << endl; _a = aa._a; _b = aa._b; _c = aa._c; } int _a = 1; int _b = 2; int _c = 3; }; //c++17标准 //auto [x, y, z] = A(2, 3, 4); A aa1(2, 3, 4); vector<A> v4; v4.push_back(aa1); for (auto [x, y, z] : v4) { cout << x << ' ' << y << ' ' << z << endl; } }struct test001 { int a = 12; int b = 32; }; struct test002 { test001 n = test001(); }; //auto [x, y] = test002();//erro auto [x] = test002(); auto [y,z] = x; cout << y << ":" << z << endl;注意:这里结构化绑定不能直接访问类成员中成员变量,需要一步一步分解。
迭代器失效
vector迭代器失效,指的是不能继续使用迭代器(扩容会改变迭代器指针是位置,而对应的pos位置指针不会给你改变,会导致野指针)对于迭代器失效,就可以使用返回值来保留当前位置(更新就能继续使用)
vector的模拟
迭代器:
namespace bear { 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; } private: iterator _start; // 指向数据块的开始 iterator _finish; // 指向有效数据的尾 iterator _endOfStorage; // 指向存储容量的尾 }; }容器:
//size size_t size() const { return _finish - _start; } //capacity size_t capacity() const { return _endOfStorage - _start; } //扩容 void reserve(size_t n) { if (n > capacity()) { T* tmp = new T[n]; size_t old_size = _finish - _start; if (_start) { //memcpy(tmp, _start,_finish-_start);//只能浅拷贝 for (size_t i = 0; i < size(); i++) { //可以调用需要深拷贝的构造 tmp[i] = _start[i]; } delete[] _start; } _start = T; _finish = _start + old_size; _endOfStorage = _start + n; } } //resize void resize(size_t n, const T& value = T()) { if (n > capacity()) { reserve(n); iterator it1 = _start + size(); while (it1 != _start + n) { (*it1) = value; ++it1; } _finish = _start + n; } else { _finish = _start + n; } }元素访问:
///////////////access/////////////////////////////// //下标引用 T& operator[](size_t pos) { assert(pos <= _finish); return _start[pos]; } const T& operator[](size_t pos)const { assert(pos <= _finish); return _start[pos]; }元素修改:
///////////////modify///////////////////////////// void push_back(const T& x) { //空间不够 if (_finish == _endOfStorage) { reserve(capacity() == 0 ? 4 : 2 * capacity()); } *_finish = x; ++_finish; } void pop_back() { --_finish; } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._start); std::swap(_endOfStorage, v._endOfStorage); } iterator insert(iterator pos, const T& x) { assert(pos >= _start); assert(pos <= _finish); if (_finish == _endOfStorage) { //保存pos相对位置(这里也是为了防止迭代器失效) size_t len = pos - _start; reserve(capacity() == 0 ? 4 : 2 * capacity()); pos = len + _start; } iterator it = _finish - 1; while (it >= pos) { *(it + 1) = *it; --it; } *_finish = x; ++_finish; //防止迭代器失效 return pos; } iterator erase(iterator pos) { assert(pos >= _start); assert(pos <= _finish); iterator it = pos+1; while (it != _finish) { *(it - 1) = *it; ++it; } --_finish; return pos;//防止迭代器失效 }构造、赋值和析构:
// construct and destroy vector() :_start(nullptr) ,_finish(nullptr) ,_endOfStorage(nullptr) { } vector(int n, const T& value = T()) { resize(n, value); } template<class InputIterator> vector(InputIterator first, InputIterator last) { while (first != last) { push_back(first); ++first; } } //拷贝 vector(const vector<T>& v) { reserve(v.capacity()); for (auto e : v) { push_back(e); } } //赋值 vector<T>& operator= (vector<T> v) { swap(v); return *this; } //析构 ~vector() { delete[] _start; _start = _finish = _endOfStorage = nullptr; }总代码综合:
#pragma once #include<iostream> #include<assert.h> namespace bear { 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()) { resize(n, value); } template<class InputIterator> vector(InputIterator first, InputIterator last) { while (first != last) { push_back(first); ++first; } } //拷贝 vector(const vector<T>& v) { reserve(v.capacity()); for (auto e : v) { push_back(e); } } //赋值 vector<T>& operator= (vector<T> v) { swap(v); return *this; } //析构 ~vector() { delete[] _start; _start = _finish = _endOfStorage = nullptr; } //size size_t size() const { return _finish - _start; } //capacity size_t capacity() const { return _endOfStorage - _start; } //扩容 void reserve(size_t n) { if (n > capacity()) { T* tmp = new T[n]; size_t old_size = _finish - _start; if (_start) { //memcpy(tmp, _start,_finish-_start);//只能浅拷贝 for (size_t i = 0; i < size(); i++) { //可以调用需要深拷贝的构造 tmp[i] = _start[i]; } delete[] _start; } _start = tmp; _finish = _start + old_size; _endOfStorage = _start + n; } } //resize void resize(size_t n, const T& value = T()) { if (n > capacity()) { reserve(n); iterator it1 = _start + size(); while (it1 != _start + n) { (*it1) = value; ++it1; } _finish = _start + n; } else { _finish = _start + n; } } ///////////////access/////////////////////////////// //下标引用 T& operator[](size_t pos) { assert(pos <= _finish); return _start[pos]; } const T& operator[](size_t pos)const { assert(pos <= _finish); return _start[pos]; } ///////////////modify///////////////////////////// void push_back(const T& x) { //空间不够 if (_finish == _endOfStorage) { reserve(capacity() == 0 ? 4 : 2 * capacity()); } *_finish = x; ++_finish; } void pop_back() { --_finish; } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._start); std::swap(_endOfStorage, v._endOfStorage); } iterator insert(iterator pos, const T& x) { assert(pos >= _start); assert(pos <= _finish); if (_finish == _endOfStorage) { //保存pos相对位置(这里也是为了防止迭代器失效) size_t len = pos - _start; reserve(capacity() == 0 ? 4 : 2 * capacity()); pos = len + _start; } iterator it = _finish - 1; while (it >= pos) { *(it + 1) = *it; --it; } *_finish = x; ++_finish; //防止迭代器失效 return pos; } iterator erase(iterator pos) { assert(pos >= _start); assert(pos <= _finish); iterator it = pos+1; while (it != _finish) { *(it - 1) = *it; ++it; } --_finish; return pos;//防止迭代器失效 } private: iterator _start; // 指向数据块的开始 iterator _finish; // 指向有效数据的尾 iterator _endOfStorage; // 指向存储容量的尾 }; }总结:
vector这里较为重要的有vector的使用和vector的模拟,其中迭代器的失效很重要,通过底层实现vector的中发现在任意删和任意插(insert,erase)中pos指向会发生改变,这就导致了迭代器的失效,这里解决这个问题就是更新pos位置,然后返回pos对应的指针。ps:vector底层实现是靠的指针,所以元素起始位置地址和vector类的地址是不一样的,vector类保存的地址是私有成员中_ptr的成员地址(&vector保存了"_ptr内部保存的地址"的地址,相当于套了一个壳),在这里只是保存了指针变量的地址,而不是指针变量指向的地址(_ptr变量内部保存的地址),但是在这个_ptr成员中指向的是数组起始位置。
vector 对象 └─ _ptr (一个指针变量,它自己有个地址 = &_ptr ≈ &v) └─ _ptr 的值 (指向堆上数组,= v.data())