如标题,给这篇文章来一个非常中二的配图,致敬在码路上不断进化的我们
一. 标准库中维克托的各种小技巧
(1).维克托一共有很多种初始化
空初始化 | vector<int> vec; | 创建空容器,后续动态添加元素 |
指定大小 | vector<int> vec(5); | 初始化为指定大小,元素默认值为 0 |
指定大小和值 | vector<int> vec(5, 10); | 初始化为指定大小,所有元素为指定值 |
列表初始化 | vector<int> vec = {1,2,3}; | C++11 起支持,直观指定初始元素 |
数组初始化 | vector<int> vec(arr, arr+5); | 从数组指定范围复制元素 |
复制初始化 | vector<int> vec2(vec1); | 创建已有 vector 的副本 |
迭代器范围初始化 | vector<int> vec(begin, end); | 从其他容器的迭代器范围复制元素 |
移动初始化 | vector<int> vec(createVector()); | C++11 起支持,通过移动资源避免拷贝 |
二维 vector 初始化 | vector<vector<int>> m(3,4,0); | 创建多维数组,常用于矩阵等场景 |
assign 方法初始化 | vec.assign(5, 10); | 先创建空 vector,再通过 assign 赋值 |
(2)维克托 中operator[]和at的比较
operator []允许我们像访问数组一样访问维克托,但也缺少对边界条件的检测
vertor::at会检测是否越界,如果越界会抛出异常
int main()
{
vector<int> v(5, 10);
int b=v.at(5);
}
相对来说,at是比operator[ ]更高效的访问方式
二.对vector增删查改的手撕实现
namespace theshy
{
template<class T>
class vector
{
typedef T* iterator;
typedef const T* const_iterator;
public:
iterator begin()
{
return arr;
}
iterator end()
{
return arr + _size;
}
const_iterator begin()const
{
return arr;
}
const_iterator end()const
{
return arr + _size;
}
vector(int size = 0)
:_capacity(size)
{
_size = 0;
arr = new T[size];
cout << "构造函数" << endl;
}
vector(const vector<T>& a)
{
_size = a._size;
_capacity = a._capacity;
arr = new T[_size];
for (int i = 0; i < _size; i++)
{
arr[i] = a.arr[i];
}
cout << "拷贝构造" << endl;
}
~vector()
{
delete[] arr;
arr = nullptr;
_size = 0;
_capacity = 0;
cout << "析构函数" << endl;
}
vector<T>& operator=(const vector<T>& a)
{
delete[] arr;
arr = nullptr;
_size = 0;
_capacity = 0;
_size = a._size;
_capacity = a._capacity;
arr = new T[_size];
for (int i = 0; i < _size; i++)
{
arr[i] = a.arr[i];
}
cout << "赋值重载函数" << endl;
}
void swap(vector<T>& v)
{
std::swap(v.arr, arr);
std::swap(v._size, _size);
std::swap(v._capacity, _capacity);
}
void pushback(const T& a )
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
arr[_size] = a;
_size++;
}
void insert(int pos, int n, const T& a)//在指定位置之前插入数据,分三步,一步是开空间,一步是移动数据,一步是插入数据
//移动时避免覆盖从后往前移动
///数组的分块[pos,pos+n-1]插入,[pos+n,size+n-1]移动区
{
reserve(_size + n );
for (int i = _size -1; i >= pos; i--)
{
arr[i + n] = arr[i];
}
for (int i = pos; i <= pos + n - 1; i++)
{
arr[i] = a;
}
_size += n;
}
void insert(int pos, const T& a)
//插入a
{
reserve(_size + 1);
for (int i = _size - 1; i >= pos; i--)
{
arr[i +1] = arr[i];
}
arr[pos] = a;
_size += 1;
}
void popback()//尾删
{
assert(_size != 0);
_size--;
}
void erase(int pos)
{
for (int i = pos + 1; i <= _size - 1; i++)
{
arr[i - 1] = arr[i];
}
_size--;
}//数组分块,[pos]为删除数据,[pos+1,size-1]向前移移动一位
void reserve(int b )
{
if (b > _capacity) {
//扩容
vector<T> a(b);
a._size = _size;
for (int i = 0; i < _size; i++)
{
a.arr[i] = arr[i];
}
a.swap(*this);
cout << "capacity make a change for:" << _capacity << endl;
}
return;
}// 这里尝试用智能指针的思路去优化代码行数,但是发现了一个问题void reserve(int a )
//{
// if (a > _capacity) {
// //扩容
// vector<T> a(a);
// a._size = _size;
// for (int i = 0; i < _size; i++)
// {
// a.arr[i] = arr[i];
// }
// a.swap(*this);
// cout << "capacity make a change for:" << _capacity << endl;
// }
// return;
//}这里发现当使用reserve的时候capacity一直不发生变化,究其原因是因为vector<T> a(a) 和形参a重名了
//导致匹配上了拷贝构造函数,一直给自己赋值,这提醒我必须重视命名
T& operator[](int pos)
{
assert(pos < _size);
return arr[pos];
}
int size()
{
return _size;
}
int capacity()
{
return _capacity;
}//注意这里不能加引用,因为随便修改成员是非常危险的行为
void print()
{
T* p = arr;
for (int i = 0; i < _size; i++)
{
cout << *p << endl;
p++;
}
cout << "size=" << _size << endl;
cout << "capacity=" << _capacity << endl;
}
protected:
T* arr;
int _size;
int _capacity;
};
}
int main()
{
vector<int> v(5, 10);
int b=v.at(5);
}
/*
比尔盖茨曾说过:“用代码行数来衡量程序的开发进度, 就好比用重量来衡量飞机的制造进度。”*/
比上次string手撕进步的点是,更加熟练了。提高了对模板的运用能力 。在对数组分块的问题上吸取了上次逻辑不明的教训,这次先理顺逻辑再写代码
不足是在命名问题上还是太粗心了,下次得把命名规范起来,像标准库靠进
三.迭代器失效问题
会引起其底层空间改变的操作,都有可能是迭代器失效
,比如:
resize
、
reserve
、
insert
、
assign
、
push_back
等。
一下列出两种迭代器失效的情况
情况一
int main()
{
vector<int> v(5, 10);
auto it = v.begin();
v.resize(100, 8);
while (it != v.end())
{
cout << *it << endl;
++it;
}
return 0;
}
这里会发生迭代器失效的情况,因为resize之后之前it指向的空间就失效了。
情况二
int main()
{
int a[] = { 1,1,5,5,5,5,5,5,5,5,5,5,5,7 };
vector<int> v(a,a+14);
auto it = find(v.begin(), v.end(), 7);
v.erase(it);
cout << *it << endl;
return 0;
}
这里也会发生迭代器失效的情况。因为it指向的是被删除的位置。一般是没有问题的,但是如果这恰好是数组中最后一个位置的话就会发生越界的情况。
解决迭代器失效的办法:重新给迭代器赋值一下就好了