构造
这里默认的拷贝构造是浅拷贝,程序结束会析构俩次,所以崩溃
我们这需要自己设置一个深拷贝
传统写法:
vector(const vector<T>& v) { _start = new T[v.size()]; memcpy(_start, v._start, sizeof(T) * v.size()); _finish = _start + v.size(); _end_of_storage = _start + v.size(); }
也可这样写
vector(const vector<T>& v) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) { reserve(v.size()); for (const auto& e : v) { push_back(e); } }
现代写法:
vector里面有一个构造是支持迭代器区间构造
之前我们已经有了类模板
现在我们在类模板,成员函数里面再加一个模板
也就是说现在的这个拷贝构造函数里面可以用双重模板,在定义模板是因为我们需要一个迭代区间,而之前我们已经有了iterator,现在是InputIterator,这样写代表我们不仅可以用T类型模板,iterator迭代器,也可以用其他模板和其他迭代器,这样比较灵活
便于我们实现这种情况
当前函数写这样运行的时候会直接报错,因为如果编译器没有对s进行初始化,push_back要调用reverse,删除旧数组空间的时候会报错
因此我们要对s进行初始化
template <class InputIterator> vector(InputIterator first, InputIterator last) : _start(nullptr) , _finish(nullptr) , _end_of_storage(nullptr) { while (first != last) { push_back(*first); ++first; } }
现代写法:
template <class InputIterator> vector(InputIterator first, InputIterator last) : _start(nullptr) , _finish(nullptr) , _end_of_storage(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(_end_of_storage, v._end_of_storage); } vector(const vector<T>& v) :_start(nullptr) , _finish(nullptr) , _end_of_storage(nullptr) { vector<T> tmp(v.begin(), v.end()); swap(tmp); }
vector<T>& operator=(vector<T> v) { swap(v); return *this; }
这里传参的时候不进行引用传参,他会调用拷贝构造,根据我们写的拷贝可知,实参会被删除,保留形参,v就是保留下来的形参,然后跟一交换即可,这样比较方便
思考题
vector(size_t n, const T& val = T()) 插入n个val,val是T类型
使用N个vakue去构造,T()是T类型的匿名对象,匿名对象就要调用默认构造,如果是Date这些自定义类型,就需要我们人为的把构造函数写好,如果T是int,int也有自己的默认构造,内置类型也有自己的默认构造
vector(size_t n, const T& val = T()) :_start(nullptr) ,_finish(nullptr) ,_end_of_storage(nullptr) { reserve(n); for (int i = 0; i < n; i++) { push_back(val); } }
当这样初始化会报错
报错信息在这一行:非法的间接寻址
当编译器进行类型匹配的时候,如果只传了一个参数,编译器会把那个参数传给最左边的形参(前面博客有说过)
而传俩个参数都是int类型,编译器在进行类型匹配的时候会找最匹配的类型来匹配
一个参数是unsigned int,一个是int
对于这里传参传的是int,int,这个对拷贝构造来说特别匹配,first 和last都是int,然后对int解引用就报错
而下面这个不报错因为,对于拷贝构造来说不匹配,所以它进不去拷贝构造
resize
void resize(size_t n, const T& val = T()) { if (n > capacity()) { reserve(n); } if (n > _finish) { while (_finish < _start + n) { *finish = val; ++_finish; } } else { _finish = _start + n; } }
reserve对_finish不是很敏感,resize可以改变_finish
vector<vevtor<T>>深浅拷贝问题 vector(const vector<T>& v) //拷贝构造函数 { _start = new T[v.size()]; memcpy(_start, v._start, sizeof(T) * v.size()); _finish = _start + v.size(); _end_of_storage = _start + v.size(); } class Solution { public: vector<vector<int>> generate(int numRows) { vector<vector<int>> vv; vv.resize(numRows); for (int i = 0; i < vv.size(); ++i) { vv[i].resize(i + 1, 0); vv[i].front() = vv[i].back() = 1; } for (size_t i = 0; i < vv.size(); ++i) { for (size_t j = 0; j < vv[i].size(); ++j) { if (vv[i][j] == 0) { vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1]; } } } return vv; } }; void test_vector1() { Solution().generate(5); }
上面代码是拷贝构造函数以及杨辉三角的调用
程序直接崩溃
这是因为在函数返回的时候要调用一次拷贝构造,而该拷贝构造对外面是深拷贝,对里面是浅拷贝
这种拷贝方式只是对最外层完成了慎深拷贝,而对于里层确实浅拷贝
问题出在了memcpy上,memcpy是一个字节一个字节往过拷贝,导致这里里层的_start指向同一块空间,析构的时候会析构俩次,因此这里不能用memcpy
这样修改拷贝构造,程序即可正常运行
vector(const vector<T>& v) { _start = new T[v.size()]; // memcpy(_start, v._start, sizeof(T) * v.size()); for (int i = 0; i < v.size(); ++i) { _start[i] = v._start[i]; } _finish = _start + v.size(); _end_of_storage = _start + v.size(); }
防止reserve出现这种问题,对reserve也可进行修改
void reserve(size_t n) { if (n > capacity()) { size_t sz = size(); T* tmp = new T[n]; if (_start) { //memcpy(tmp, _start, sizeof(T) * sz); for (size_t i = 0; i < sz; ++i) { tmp[i] = _start[i]; } delete[] _start; } _start = tmp; _finish = _start + sz; _end_of_storage = _start + n; } }
习题 电话号码的字母组合
17. 电话号码的字母组合 - 力扣(LeetCode)
class Solution { char *numToStr[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"}; public: void Combine(string digits,int di,vector<string>& retv,string CombinStr) { if(di==digits.size()) { retv.push_back(CombinStr); return ; } int num=digits[di]-'0'; string str=numToStr[num]; for(auto ch:str) { Combine(digits,di+1,retv,CombinStr+ch); } } vector<string> letterCombinations(string digits) { vector<string> v; if(digits.empty()) return v; string str; Combine(digits,0,v,str); return v; } };
str是一个用来临时存储组合的结果,digits是用户输入的数字,di是下标,如digits是“238”,di=0,就代表表示字符2,所以要给-'0',把每个按键对应的字母存到一个数组中,通过下标来访问
习题 删除有序数组中的重复项
26. 删除有序数组中的重复项 - 力扣(LeetCode)
class Solution { public: int removeDuplicates(vector<int>& nums) { int src=0; int dst=0; while(src<nums.size()) { if(nums[src]==nums[dst]) { ++src; } else { ++dst; nums[dst]=nums[src]; src++; } } return dst+1; } };