目录
学习语言,第一境界是会用,第二境界是理解为什么,第三境界是能扩展。所有的一切,例如做题,学习、看书、各种行为都是手段,为着我们更好的运用。因为,最终都是为了更好的去理解某个东西,以便于更好的去改造这个世界。理解世界,而不是解释世界;改造世界,而不是跟从世界。
目录
一、vector的介绍以及常用接口
1、成员函数
//1、初始化的四种方式
vector<int> v1;//空参
vector<int>v2(5,1);//创建5个1
vector<int>v3 = {1,2,3,4,5};//初始化
vector<int>v4(v3);//拷贝
vector<int>v5(v3.begin(), v3.end());//迭代器拷贝
2、迭代器
//1、begin和end用法
vector<int>v = {1,2,3,4,5};
vector<int>::iterator it = v.begin();
printf("begin和end:");
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//2、rbegin和rend(r就是反转的意思,rbegin为最后位置,rend为最前位置)
vector<int>v1 = { 1,2,3,4,5 };
vector<int>::reverse_iterator it1 = v1.rbegin();
printf("rbegin和rend:");
while (it1 != v1.rend())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
//3、cbegin和cend->const对象使用
const vector<int>v2 = { 1,2,3,4,5 };
vector<int>::const_iterator it2 = v2.cbegin();
printf("cbegin和cend:");
while (it2 != v2.cend())
{
cout << *it2 << " ";
++it2;
}
cout << endl;
//4、crbegin和crend->const对象使用
const vector<int>v3 = { 1,2,3,4,5 };
vector<int>::const_reverse_iterator it3 = v3.crbegin();
printf("crbegin和crend:");
while (it3 != v3.crend())
{
cout << *it3 << " ";
++it3;
}
cout << endl;
a、下标方括号
vector<int>v2(10,1);
for (int i = 0; i <v2.size();++i )
{
cout << v2[i] << " ";
}
cout << endl;
b、迭代器
vector<int>v(10, 1);
std::vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
c、范围for(替换为迭代器)
vector<int>v(10, 1);
for (auto e : v)
{
cout << e <<" ";
}
cout << endl;
resize,开空间
3、Capacity
1、reserve
//改变容量
vector<int>v(10, 1);
v.reserve(100);
cout << v.capacity() <<endl;
2、resize
//改变size大小
vector<int>v(10, 1);
v.resize(100);
cout << v.capacity() <<endl;
3、shrink_to_fit
//改变size到合适的尺寸
vector<int>v(10, 1);
v.reserve(100);
v.shrink_to_fit();
cout << v.capacity() <<endl;
//1、begin和end用法
vector<int>v = {1,2,3,4,5};
vector<int>::iterator it = v.begin();
printf("begin和end:");
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//2、rbegin和rend(r就是反转的意思,rbegin为最后位置,rend为最前位置)
vector<int>v1 = { 1,2,3,4,5 };
vector<int>::reverse_iterator it1 = v1.rbegin();
printf("rbegin和rend:");
while (it1 != v1.rend())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
//3、cbegin和cend->const对象使用
const vector<int>v2 = { 1,2,3,4,5 };
vector<int>::const_iterator it2 = v2.cbegin();
printf("cbegin和cend:");
while (it2 != v2.cend())
{
cout << *it2 << " ";
++it2;
}
cout << endl;
//4、crbegin和crend->const对象使用
const vector<int>v3 = { 1,2,3,4,5 };
vector<int>::const_reverse_iterator it3 = v3.crbegin();
printf("crbegin和crend:");
while (it3 != v3.crend())
{
cout << *it3 << " ";
++it3;
}
cout << endl;
4、内存管理
//1、size返回元素个数
//2、max_size返回该类型顺序表可存最大个数
//3、resize,小于原size不处理,大于原size则扩容
vector<int> v{ 1,2,3,4,5 };
v.reserve(0);
cout << v.capacity() << endl;
v.reserve(20);
cout << v.capacity() << endl;
//4、empty判空
v.empty();
//5、shrink_to_fit 空间过大,但是元素不够,就会缩减多余空间,即所谓自适应
//6、reserve,小于原size不处理,大于原size则扩容
v.reserve(1);
cout << v.capacity() << endl;
v.reserve(20);
cout << v.capacity() << endl;
二、底层原理
vector是一个数组,但是多了一个模板
1、push_back:
1、扩容判断(直接使用reserve)
2、插入
只是现在要注意的是,控制的对象是T模板,把T联想为int就很好写了
finish指向结束数据的下一个位置
扩容的时候,旧的空间销毁了,但是finish指针还是指向旧的空间的位置
因为新的空间的开始位置加上原来数据的size计算出当前空间的大小
要记录size的大小
迭代器iterator为什么前面要跟类域?
当报报错为意外标识符时,一般都是类型无法识别
因为每一个类型都提供了相应的迭代器,各自的迭代器是不一样的
为了区分到底是用的是谁的迭代器,所以需要指定类域去找iterator
但是,如果指定的类域是一个模板,没有进行实例化
那么编译器到底找谁的iteerator不知道,所以就无法识别
而iterator本质其实就是一个指针,宏定义的指针。
而指定类域访问的东西,有可能是一个静态成员变量,也有可能是一个类型
但是编译器无法区分
所以,为了解决这个问题
有两个解决方案:
1、用auto自动推导类型,将之视为类型,而不是静态变量,消除歧义
2、类域使用typename,同理,也是将之视为类型,而不是静态变量,消除歧义
2、empty
reserve()#一般不会进行缩容
如果n>_capacity,那就需要扩容
resize()
capacity() return _finish - _start
3、insert
1、检查pos
2、从pos位置开始往后挪动数据,判断条件为挪动到新的pos位置
3、将x插入pos位置
4、容量+1,即finish位置加1
上述还是有问题
什么问题?
当传入pos时,这个pos指向的是原来的空间位置
但是,扩容会把原空间销毁,pos变成野指针
再将pos和新的空间位置对应,就会出问题
这个问题,叫做迭代器失效
所以,空间更新的时候,pos要跟着更新
记录pos和start的距离len
然后空间更新后,对新空间的start+len(写完insert之后,push_back就可以复用了)
4、erase
1、检查pos位置(不能等于finish,因为这个位置不是有效位置)
从pos位置开始,删除n个,第二个参数可以设置为缺省
实现思路:
从pos位置往前挪动n次数即可
无非就是从pos往后n个往前挪动n的距离罢了
就是需要控制一下下标的位置(此时尾删也可以复用erase,就是删除最后一个位置罢了)
5、resize
三种情况,大于size,扩容+插入
小于size,删除
等于size,不动
当大于时,插入的东西,是什么?有可能是int,有可能是string,所以也要写成模板的形式,而不能写死
用匿名对象
匿名对象:类型()(类型加一个括号)
什么意思?就是一个没有参数的匿名对象,这个对象会去调用对应本身的构造函数,也就是完成了自己的初始化
同时,模板推导也会变成这个类型
但是,自定义类型,上述可以
但是,如果是内置类型呢?
内置类型是没有构造的
但是,为了配对模板,内置类型被迫跟着升级
也就是内置类型也有了构造和析构
只是我们平时初始化的时候,没有可以去显示书写调用罢了
字符0的ASCII码是48
对大于情况,直接干一个reserve,因为reserve会对n进行检查,n大于capacity就会直接扩容
然后,对finish位置开始,知道start+n位置插入数据即可
删除就是更新finish位置,start + n
6、迭代器
begin()返回开始位置,end()返回size位置,就这么简单。
赋值oparetor=:复用拷贝构造,传值拷贝,直接交换
传值返回,返回临时变量
所以不能++和--,因为不会改变原始的值
7、函数寻址错误
当两个函数模板都匹配时,会导致错误。函数寻址错误
如何解决?再写一个重载函数来处理模板冲突匹配的问题
函数重载只对形参处理,因此返回值不同不构成重载
8、隐式类型转换
参数最好把const加上,这样才能支持隐式类型转换
否则当我们初始化用常量直接进行初始化时,就不能兼容
vector<int> v = {1,2,3,4,5};
string s1("hellow world");
为什么可以像上述这样初始化?
因为上述在右边在会调用一个init_list的函数,进行构造
然后再将这个类型返回
本质是单参数函数会进行隐式类型转换
9、string不能用memcpy进行拷贝
当vectror是一个striing类型时,
string内有_str指针
而,memcpy按字节拷贝
对于其他值,_start,_finish没有问题
但是对于_str就会有问题,因为只是改变了指针指向
而没有开辟出空间
那么,当旧的空间被释放析构后,指针变成野指针
所以,问题核心是,当有自定义类型需要空间拷贝时
用mmemcpy会出错
怎么解决呢?
因为问题出再memcpy,所以换掉即可
直接使用for进行赋值
一个对象一个对象的赋值
10、迭代器失效
vectot迭代器野指针失效
当时扩容新空间以后,迭代器依旧指向旧的空间,但是旧空间已被析构释放,导致错误
但是在内部不是已经更细了吗?
因为it作为形参进行传值
更新是使用形参pos进行的,pos是形参,形参不改变实参it
怎么处理?
不处理
放弃旧的迭代器,新的空间,创建一个新的迭代器去访问(传值返回,为临时变量,不能被引用 )迭代器失效设计的函数:insert、erase、push_back、resize
上述函数都旧空间非释放,iterator变成野指针的问题
所以要进行更新
list:insert不会失效,因为插入并未涉及释放空间
但是erase就会失效,因为涉及释放原来空间
VS处理的十分暴力:erase以后的迭代器直接不让你用
那么要怎么处理呢?
不对it进行++?
也不行
因为,如果erase以后进行了缩容呢?
这个迭代器依旧是指向旧的空间,此时就不能用这个迭代器了
所以,要用新的迭代器
而erase的返回值是删除位置的下一个数据的位置
我们就可以更新迭代器it
就是将迭代器的it更新为erase的返回值
对于顺序表而言,尽量不要使用头插头删,效率极低
vector这里只有尾删尾插
11、算法库的find函数
如果不存在找的值,返回的是传入迭代器的最后一个位置
这个位置,因为是数组,例如说10个元素,最后一个end是10,但是下标只有9
所以10实际上是一个不存在的区间
为什么要用算法库的fnd?
而strinig要自己的find?
因为string要找字串,算法库不支持
12、vector二维数组
怎么理解vector<vector<int>>
先创建一个vector<int>对象
再将上述对象作为vector的参数
也就是说,vector的对象不是int,而是一个个vector<int>对象
它和二维数组很像,但是底层是不一样的
二维数组底层是一个一维数组
二维数组名是第一行的首地址
a[i][j]
第i行,第j列的元素
元素位置 = i*每一列的元素 + j 或者 *((a+i) + j)
vector<vector<int>> vv;
vv[i][j];
i返回的是:vv.operator[](i) #返回vector<int>的指针
j返回的是:vv.operator[](i).vector[](j) #返回vector<int>下的数组指针
力扣的规定:
返回一维数组,返回数组的个数
返回二维数组,返回行数和每一列的个数
三、底层模拟代码
#pragma once
#pragma once
#include<iostream>
#include<stdio.h>
#include<assert.h>
using namespace std;
namespace bit
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
iterator rbegin()
{
return _finish;
}
iterator rend()
{
return _start;
}
const_iterator cbegin()
{
return _start;
}
const_iterator cend() const
{
return _finish;
}
// construct and destroy
//空参
vector()
{
}
//v1(n,v2),将v2的n个拷贝到v1
vector(int n, const T& value = T())
{
reserve(n);
for (size_t i = 0;i < n;++i)
{
push_back(value);
}
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
// v1(v2)
vector(const vector<T>& v)
{
reserve(v.capacity());
for (auto& e : v)
{
push_back(e);
}
}
// v1(v2)
vector( vector<T>& v)
{
reserve(v.capacity());
for (auto& e : v)
{
push_back(e);
}
}
//析构
~vector()
{
delete[] _start;
_start = _finish = _endOfStorage = nullptr;
}
// capacity
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endOfStorage - _start;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size();
T* tmp = new T[n];
for (size_t i = 0;i< old_size;++i)
{
tmp[i] = _start[i];
}
delete[] _start;
_start = tmp;
_finish = tmp + old_size;
_endOfStorage = tmp + n;
}
}
void resize(size_t n, const T& value = T())
{
if (n > capacity())
{
//直接扩容,剩下就是插入数据
reserve(n);
while(_finish < _start + n)
{
*_finish = value;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
///access///
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
///modify/
void swap(vector<T>& v)
{
swap(_start, v._start);
swap(_finish, v._finish);
swap(_endOfStorage, v._endOfStorage);
}
void erase(iterator pos)
{
assert(pos < _finish);
assert(pos >= _start);
iterator it = pos + 1;
while (it <= _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
}
void insert(iterator pos, const T& x)
{
assert(pos <= _finish);
assert(pos >= _start);
//扩容
if (_finish == _endOfStorage)
{
//为防止迭代器失效,需要记录位置,更新pos位置
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator it = _finish - 1;
while (it >= pos)
{
*(it + 1) = *it;
--it;
}
*pos = x;
++_finish;
}
void push_back(const T& x)
{
insert(_finish,x);
}
void pop_back()
{
/* assert();
if (_start != _finish)
--_finish;*/
erase(_finish);
}
/* template<class T>
void print_vector(const vector<T>& v)
{
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
}*/
private:
iterator _start = nullptr; // 指向数据块的开始
iterator _finish = nullptr; // 指向有效数据的尾
iterator _endOfStorage = nullptr; // 指向存储容量的尾
};
template <class T>
void print(const vector<T>& v)
{
for (size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << " ";
}
cout << endl;
}
void test1()
{
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);
print(v);
//vector<double> d;
//d.push_back(11.1);
//d.push_back(12.1);
//d.push_back(13.1);
//print(d);
//vector<char> s;
//s.push_back('a');
//s.push_back('b');
//s.push_back('c');
//print(s);
//v.insert(v.begin() + 2, 100);
//print(v);
//v.erase(v.begin() + 2);
//print(v);
//v.resize(4);
//cout << v.capacity() << endl;
//print(v);
//cout << v.capacity() << endl;
//v.resize(10, 1);
//cout << v.capacity() << endl;
//print(v);
//vector<int> v1(v);
//print(v1);
vector<int> v2(v.begin(),v.end());
print(v2);
}
}