目录
1.vector注意事项
2.vector基本模拟实现
insert迭代器失效
erase迭代器失效场景
erase正确写法
总结:insert/erase不要直接访问pos,必须先更新pos位置再访问,不然就会出现迭代器失效
3.拷贝构造:深拷贝
3.2 内置类型升级
3.3 非法的间接寻址
4.赋值运算符
1.vector注意事项
vector构造函数
default(1):不仅仅允许在模板参数中自己定义空间配置器allocator,还允许在构造函数时自定义传递
fill(2):n个val初始vector,size_type(==size_t)和value_type(第一个模板模板参数)是vector tpyedef的成员类型
range(3):迭代器区间构造
copy(4):拷贝构造
遍历:[]
[]不同于at,at越界抛异常,[]越界assert
返回pos位置数据的引用
vector迭代器类模板不能用类名 ,得用模板类型
void testvector1()
{
vector v1;
vector v2(10, 1);
vector v3(v2);
for (size_t i = 0; i < v2.size(); ++i)
{
cout << ++v2[i] << " ";//[]返回的数据是对应数据的引用
}
cout << endl;
vector::iterator it = v2.begin();
while (it != v2.end())
{
cout << --(*it) << " ";//迭代器可以修改和遍历,但是注意优先级
++it;
}
cout << endl;
for (auto ch : v2)
{
cout<<--ch<<" ";
}
}
push_back && pop_back
vector只提供了尾插尾删
const value_type& val == const T& val(T可以是任意类型)
void testvector4()
{
vector s;
s.push_back("zhangsan");//隐式类型转换
string str1("lisi");
s.push_back(str1);
s.push_back(string("wangwu"));//匿名结构+隐式类型转换
for (auto& e : s)//避免深拷贝
{
cout << e << " ";
}
}
insert
vector给的不再是下标位置,而是迭代器
erase
void testvector2()
{
vector v1(10,2);
vector::iterator pos = find(v1.begin(), v1.end(),2);
if (pos != v1.end())//没找到返回end
{
v1.insert(pos, 3);
}
for (auto ch : v1)
{
cout << ch << " ";
}
cout << endl;
pos = find(v1.begin(), v1.end(), 3);
if (pos != v1.end())//没找到返回end
{
v1.erase(pos);
}
for (auto ch : v1)
{
cout << ch << " ";
}
}
sort快排(包含在algorithm头文件中)
传迭代器区间即可排序,默认升序
逆序包头文件
void testvector3()
{
vector v1;
v1.push_back(53);
v1.push_back(41);
v1.push_back(32);
v1.push_back(512);
greater gt;
less ls;//小堆升序
sort(v1.begin(), v1.end(), gt);
//sort(v1.begin(), v1.end(),greater());//匿名对象
for (auto e : v1)
{
cout << e << " ";
e++;
}
注意:vector没有重载流提取和流插入
resize和reserve
resize改变size和capacity,reserve只改变capacity
2.vector基本模拟实现
vector重要的成员变量是三个迭代器,迭代器是原生指针
begin是第一个位置,finish是最后一个数据的下一个位置,end_of_storage是最后一个位置
#include
#include
#include
#include
#include
using namespace std;
namespace My_Vector
{
template
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
vector()
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{}
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
size_t size() const
{
return _finish - _start;
}
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 reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * sz);//memcpy是浅拷贝
for(size_t i = 0;i < sz;++i)//完成vector的数据深拷贝,防止析构两次
{
tmp[i] = _start[i];//调用对应T类型的赋值运算符完成深拷贝
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;//如果写成+size(),_strat已经改变结果会出错
_end_of_storage = _start + n;
}
}
void resize(size_t n,const T& val = T())
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
while (_finish < _start + n)//初始化填入数据
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
void push_back(const T& v)
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = v;
_finish++;
}
void pop_back()
{
assert(_start < _finish);
--_finish;
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
}
}
insert迭代器失效
vector没有find
string的find需求不一样(找一个字符或者子串)
vector,list...的find需求是一样的,于是写成了算法,在algorithm头文件中,只需传迭代器即可,注意左闭右开
找不到返回last,第二个模板参数
错误的insert模拟实现
void insert(iterator pos,const T& x)
{
assert(pos >= _start && pos <= _finish);//=finish 为尾插
if (_finish == _end_of_storage)
{
//size_t sz = pos - _start;//解决pos失效问题,不写扩容pos失效出问题
reserve(capacity() == 0 ? 4 : capacity() * 2);
//pos = _start + sz;//解决pos失效问题,更新pos位置
}
iterator tail = _finish - 1;
while (tail >= pos)
{
*(tail + 1) = *tail;
--tail;
}
*pos = x;
_finish++;
}
当insert后出现扩容的情况,便会发生迭代器失效
原因:扩容后_start改变(New开辟新空间),旧空间释放,pos迭代器野指针,解应用野指针报错
解决方法:更新insert内部pos位置
第二个问题:pos指向的位置已经不是原来的值,数据发生挪动。insert内部pos修改并不会影响外面实参pos的位置,insert扩容时下次继续对pos位置insert报错
错误程序:在所有的偶数前面插入一个偶数的二倍
void test_vector4()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
auto it = v1.begin();
while (it != v1.end())//在所有的偶数前面插入一个偶数二倍
{
if (*it % 2 == 0)
{
v1.insert(it, *it * 2);
}
++it;
}
for (auto e : v1)
{
cout << e << " ";
}
}
程序崩溃,原因在于插入后pos指向新增insert插入的位置,++it == 2,死循环扩容后pos访问野指针
错误解决方法:void insert(iterator& pos,const T& x)
以下情况无法使用iterator&:
头插时v1.insert(v1.begin(),1);原因在于begin函数是传值返回,生成临时变量,具有常性(引用和指针存在权限问题)
解决方法:接收返回值新插入元素的迭代器位置,更新it
iterator insert(iterator pos,const T& x)
{
assert(pos >= _start && pos <= _finish);//=finish 为尾插
if (_finish == _end_of_storage)
{
size_t sz = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + sz;//解决pos失效问题
}
iterator tail = _finish - 1;
while (tail >= pos)
{
*(tail + 1) = *tail;
--tail;
}
*pos = x;
_finish++;
return pos;
}
void test_vector4()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
auto it = v1.begin();
while (it != v1.end())//在所有的偶数前面插入一个偶数二倍
{
if (*it % 2 == 0)
{
it = v1.insert(it, *it * 2);
++it;//++第一次跳过新插入的值
++it;//++第二次跳过判断的值
}
else
{
++it;
}
}
for (auto e : v1)
{
cout << e << " ";
}
}
erase迭代器失效场景
void erase(iterator pos)
{
assert(pos >= _start && pos < _finish);//开区间
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
}
要求删除所有偶数:程序崩溃
void test_vector3()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
++it;
}
for (auto e : v1)
{
cout << e << " ";
}
}
要求删除所有偶数:程序正常运行,结果正确
void test_vector4()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
vector::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
++it;
}
for (auto e : v1)
{
cout << e << " ";
}
}
运气好
要求删除所有偶数:程序正常运行,结果错误
void test_vector5()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(4);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
vector::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
++it;
}
for (auto e : v1)
{
cout << e << " ";
}
}
erase正确写法
要求删除所有偶数,解决方法:带返回值,返回删除元素的下一个位置迭代器
同时,erase接收返回值,更新下标
iterator erase(iterator pos)
{
assert(pos >= _start && pos < _finish);//开区间
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
void test_vector3()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(4);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
vector::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
it = v1.erase(it);
}
else
{
++it;
}
}
for (auto e : v1)
{
cout << e << " ";
}
}
3.拷贝构造:深拷贝
三个类型都是内置类型(指针),只能完成值拷贝
两种深拷贝写法
注意:不能使用memcpy,vector
vector(const vector& v)
{
_start = new T[v.size()];
//memcpy(_start, v._start, sizeof(T) * v.size());
for (size_t i = 0; i < v.size(); ++i)
{
_start[i] = v._start[i];//内部调用赋值完成深拷贝
}
_finish = _start + v.size();
_end_of_storage = _start + v.size();
}
vector(const vector& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(v.size());
for (const auto& e : v)
{
push_back(e);
}
}
现代写法需要带参构造函数,我们需要再实现一个迭代器区间构造函数
类模板的成员函数还能增添模板,成员函数中可以使用双重模板
再次定义的模板需要迭代器区间,无法使用iterator的原因是不知道模板类型,对象可能不是vector,写成模板可以用其他容器迭代器区间构造,只要迭代器解应用数据类型匹配即可
template
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while(first != last)
{
push_back(*first);
++first;
}
}
void swap(vector& v)
{
std::swap(_start,v._start);
std::swap(_finish,v._finish);
std::swap(_end_of_storage,v._end_of_storage);
}
vector(const vector& v)//v2 = v1
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector tmp(v.begin(), v.end());//调用模板构造函数
swap(tmp);
}
3.2 内置类型升级
模板的出现让内置类型也有默认构造函数
int i = 0;
int j = int();//0
int w = int(10);
3.3 非法的间接寻址
两个构造函数
vector(size_t n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n);
for (size_t i = 0 ; i < n ; ++i)
{
push_back(val);
}
}
template
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while(first != last)
{
push_back(*first);
++first;
}
}
传两个参数报错:非法的间接寻址
原因在于实例化时编译器寻找最匹配的构造函数,10,1都是int,和半缺省的size_t和T不是最匹配,而和InputIterator模板最匹配,但并不是迭代器区间,不能解应用报错
解决方法,新增一个int重载即可
void test_vector7()
{
//vector v(10, 1);//报错,
vector v1(10);
vector v2(10, 'a');
}
4.赋值运算符
赋值是存在对象,拷贝构造是不存在对象
传参是构造,直接swap现代写法
vector& operator=(vector v)//v2 = v1;
{
swap(v);
return *this;
}