1.vector
- vector 顺序表
- string 有\0 更贴合字符串的操作 单个信息
- vector<char> 更广泛
2.使用
1°四个默认成员函数
构造 拷贝构造 析构 赋值
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void test_vector1()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector <int> v2(v1);
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
for (size_t i = 0; i < v2.size(); ++i)
{
cout << v2[i] << " ";
}
cout << endl;
vector<int> v3;
v3.push_back(10);
v3.push_back(20);
v3.push_back(30);
v3.push_back(40);
v1 = v3;
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
}
size拿到大小
operator[ ]拿到相关数据
push_back插入
2°三种遍历方式
void test_vector2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//遍历修改数据
//1.operator[] + size
for (size_t i = 0; i < v.size(); ++i)
{
v[i] *= 2;//写
cout << v[i] << " ";//读
}
cout << endl;
//2.迭代器
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//3.范围for->被编译器替换成迭代器的方式支持的
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
3°迭代器
//2.只可读
void print_vector(const vector<int>& vt)
{
vector<int>::const_iterator it = vt.begin();
while (it != vt.end())
{
//*it = 1; 不能写
cout << *it << " ";
++it;
}
cout << endl;
}
//三种类型的迭代器 实际是4种 const正向 const反向 正向 反向
void test_vector3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//1.普通正向迭代器 可读可写
vector<int>::iterator it = v.begin();
while (it != v.end())
{
*it *= 2;
cout << *it << " ";
++it;
}
cout << endl;
print_vector(v);
//reverse 逆置
//reserve 保留
//3.倒着读
vector<int>::reverse_iterator rit = v.rbegin();
while (rit != v.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
4°capacity
void test_vector4()
{
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);
v.push_back(7);
cout << v.size() << endl;
cout << v.capacity() << endl;
//插入的时候会扩容 1.5倍(当前编译器) 有的是2倍
size_t sz;
std::vector<int> foo;
//foo.reserve(100);//提前开好空间
//foo.resize(100);+foo[i]=i;
sz = foo.capacity();
std::cout << "making foo grow:\n";
for (int i = 0; i < 100; ++i)
{
foo.push_back(i);
if (sz != foo.capacity())
{
sz = foo.capacity();
std::cout << "capacity changed: " << sz << '\n';
}
}
//增容次数更多 效率越低 因为每次增容都要付出代价
//2倍相对而言效率更好 但是浪费的空间更多
//面试题:vector插入数据如何实现的?
//为什么是2倍 为什么是1.5倍
//增多少是一种选择 各有利弊 均衡一点
//增的倍数过大会浪费空间 增的倍数过小会付出更多的代价
}
5°insert/erase
void test_vector5()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//v[4] = 5;//越界 会报断言
//v.at(4) = 4;//异常
v.insert(v.begin(), 0);//头插
v.insert(v.begin(), -1);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
v.erase(v.begin());//头删
for (auto e : v)
{
cout << e << " ";
}
}
begin访问头部
end访问尾部
6°pos位置删/find
void test_vector6()
{
vector<int> v;
v.push_back(10);
v.push_back(5);
v.push_back(2);
v.push_back(9);
v.push_back(50);
v.push_back(4);
v.push_back(5);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
//要求删掉5(只会删除第一个5) vector没有提供查找 algorithm(算法)提供了find
vector<int>::iterator pos = find(v.begin(), v.end(), 5);//这个find实际是一个函数模板(通用 作用于各个STL) 左闭右开[first,last)
if (pos != v.end())//所以找到了的话 判断条件写v.end()
{
v.erase(pos);
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
sort(v.begin(), v.end());//排序 底层是快排
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
7°迭代器失效
void test_vector7()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = v.begin();
v.push_back(6);
//v.push_back(7);//尾插7就会有问题
//当插入7的时候 容量不够 就会增容 释放旧空间 指向新空间
//但it没有动 导致失效
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
迭代器失效(增容后再push_back insert resize reserve可能导致迭代器失效)
因为迭代器it没有动 还是在旧空间 所以无法进行迭代器的遍历
void test_vector8()
{
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);
//要求删除容器中的所有偶数
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
//v.erase(it);
//删除it之后 it就失效了 这里的失效是it的位置不对了 再++it就不行了
//vs下报错了 是编译检查的 gcc不报错 导致结果不对
//1 2 3 4 5 6
//1 3 4 5 6
//it会跳过3 如果跳过的是偶数 就会导致结果错误
//重载
it = v.erase(it);//erase会返回删除的it的下一个位置的迭代器
}
else
{
++it;//不是偶数就跳过
}
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
需要加返回值 不加就有可能跳过偶数 加了就可以从删的下一个开始遍历 不会有漏数据的
情况
3.底层实现
1°构造
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
其实底层是顺序表 没有用size和capacity 用三个指针来代替
扫描二维码关注公众号,回复:
14588084 查看本文章
start头部指针
end尾部指针
endofstorage整个顺序表的尾部指针
2°析构
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
3°拷贝构造
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//拷贝构造第一种
//vector(const vector<int>& v)
//{
// _start = new T[v.capacity()];
// _finish = _start;
// _endofstorage = _start + v.capacity();
// //一个一个拷
// for (size_t i = 0; i < v.size(); ++i)
// {
// *_finish = v[i];
// ++_finish;
// }
//}
//拷贝构造第二种
vector(const vector<int>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
reserve(v.capacity());//先开好容量 因为尾插后会扩容
for (const auto& e : v)
push_back(e);//遍历加尾插
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
第一种:开新空间 start finish endofstorage指向正确位置 循环放数据
第二种:开新空间 直接尾插+遍历 范围for
4°赋值
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//v1 = v3
//vector<T>& operator=(const vector<T>& v)
//{
// if (this != &v)//不是自己跟自己赋值 //释放旧空间 //开新空间 //拷贝数据
// {
// delete[] _start;
// _start = new T[v.capacity()];
// memcpy(_start, v._start, sizeof(T) * v.size());
// }
// return *this;
//}
//v1 = v3 简便 现代写法 传值+交换
vector<T>& operator=(vector<T> v)
{
//swap(_start, v._start);
//swap(_finish, v._finish);
//swap(_endofstorage, v._endofstorage);
swap(v);//this默认给
return *this;
}
//库里面的swap交换代价是很大的 tmp=v1 v1=v2 v2=tmp
//三个深拷贝 代价极大 建议使用v1.swap(v2)
//所以自己实现一个swap
//只是指针进行交换
void swap(vector<T>& v)
{
::swap(_start, v._start);
::swap(_finish, v._finish);
::swap(_endofstorage, v._endofstorage);
//这是调用全局的swap 加:: 因为和函数同名
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
第一种:释放旧空间 开新空间 memcpy拷贝数据
第二种:直接进行交换 包含三个指针的交换 写一个命名空间的swap 利用std的swap进行
交换
5°begin/end
namespace szh
{
template<class T>
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;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
const修饰隐藏的this指针 可传const对象
6°reverse
扩大到n个空间
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();//size需要提前算好
T* tmp = new T[n];
if (_start)//考虑第一次进来 memcpy里面不能为空
{
//memcpy(tmp, _start, sizeof(T) * size()); 可以 但会造成深浅拷贝问题 按字节拷贝 浅拷贝
for (size_t i = 0; i < sz; ++i)//深拷贝
{
tmp[i] = _start[i];//string赋值给string 调用的是operator= 深拷贝
}
delete[] _start;
}
_start = tmp;
_finish = tmp + sz;//_finish-_start _start到了新空间 _finish还在旧空间 相减?
_endofstorage = tmp + n;//所以size要提前算好
//增完容后三指针要到合适的位置
}
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
注意:
-
size必须提前算好 不算好的话 最后endofstorage不好指向位置 因为旧空间释放后算
不了size了
-
第一次可能为空 用memcpy去拷贝的话 会有深浅拷贝问题 因为memcpy是按字节拷
拷贝的 一个一个字节进行拷贝 浅拷贝 所以要用for循环进行一个个的拷贝
-
步骤:开新空间 拷贝数据 释放旧空间 然后指针指向新空间
7°resize
reverse扩容+初始化
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
void resize(size_t n, const T& val = T())//给缺省 你不知道T是什么类型
{
if (n < size())
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
//一个一个赋值 不能memset(自定义类型不行)
//memxxx 按字节处理
//只适合初始化0
//0:
//00000000 00000000 00000000 00000000
//1:
//00000001 00000001 00000001 00000001(每个字节都初始化成1)
//0还是0 1不是1
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
- 不要用memset初始化
8°push_back
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);//容量增到newcapacity
}
*_finish = x;
++_finish;
//insert(_finish, x);
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
- 注意扩容
9°pop_back
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
void pop_back()
{
assert(_start < _finish);
--_finish;
//erase(_finish - 1);
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
- 注意断言
10°insert
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
void insert(iterator pos, const T& x)
{
assert(pos <= _finish);
//插入可能需要扩容
if (_finish == _endofstorage)
{
size_t n = pos - _start;
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
pos = _start + n;
//小心迭代器失效 扩容后空间就不一样了 pos还是在旧的空间
//所以需要把pos搞到新空间 提前算好pos的位置
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
- 提前算好pos位置 小心迭代器失效问题 pos要指向新空间
- 注意断言
11°erase
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator erase(iterator pos)
{
assert(pos < _finish);
iterator it = pos;
while (it < _finish)
{
*it = *(it + 1);
++it;
}
--_finish;
return pos;//返回当前位置的下一个位置 就是pos位置
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
-
迭代器 用*拿到数据
-
写成有返回类型的
-
不然删除的时候 可能会漏掉数据 因为返回的下一个位置不会被再次检测 写了返回类
型的 就可以被再次检测
12°operator[]
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//可读可写
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;
iterator _finish;
iterator _endofstorage;
};
}
- const和非const
13°size/capacity
namespace szh
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
- this指针不会被修改 最好加上const
14°测试
namespace szh
{
//只读迭代器
void print_vector(const vector<int>& v)
{
vector<int>::const_iterator it = v.begin();//都要变为const
while (it != v.end())//调begin和end 必须都是const
{
//不能改
cout << *it << " ";
++it;
}
cout << endl;
}
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
print_vector(v);
cout << v.size() << endl;
cout << v.capacity() << endl;
vector<int>::iterator it = v.begin();
while (it != v.end())
{
*it += 1;
cout << *it << " ";
++it;
}
cout << endl;
for (auto& e : v)
{
e -= 1; //对e改了 实际没改 加别名才能真的改变
cout << e << " ";
}
cout << endl;
for (size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << " ";
}
cout << endl;
}
void test_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);
v.insert(v.begin(), 0);//头插
print_vector(v);
//删除所有的偶数
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.erase(it);//有返回
}
else
{
++it;
}
}
print_vector(v);
}
void test_vector3()
{
vector<int> v;
v.reserve(10);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
print_vector(v);
cout << v.size() << endl;
cout << v.capacity() << endl;
cout << endl;
v.resize(4);
print_vector(v);
cout << v.size() << endl;
cout << v.capacity() << endl;
cout << endl;
v.resize(8, 8);//不给后面填值的话就会用缺省值
print_vector(v);
cout << v.size() << endl;
cout << v.capacity() << endl;
cout << endl;
v.resize(12, 12);
print_vector(v);
cout << v.size() << endl;
cout << v.capacity() << endl;
cout << endl;
}
void test_vector4()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int> v2(v1);//编译器:浅拷贝 析构 同一块空间多次释放
//要写拷贝构造 进行深拷贝
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v2[i] << " ";
}
cout << endl;
vector<int> v3;
v3.push_back(10);
v3.push_back(20);
v3.push_back(30);
v3.push_back(40);
v1 = v3;//赋值
print_vector(v1);
print_vector(v3);
}
void test_vector5()
{
vector<string> v;
v.push_back("111111111111111111111111111111");
v.push_back("222222222222222222222222222222");
v.push_back("333333333333333333333333333333");
v.push_back("444444444444444444444444444444");
//增容(调的reserve) 用的memcpy拷的(浅拷贝)(string有问题 因为拷贝的是指针) 但是拷下来和原来的指针都是指向同一块空间
//这时候去delete原来的 导致指向的那块空间释放 最后就是随机值
//更深层次的深浅拷贝问题
//关键是都指向同一块空间 如何解决?
//深拷贝 赋值操作 operator= 深拷贝
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
}
【C++】8.vector 完