vector简单介绍

目录

学习语言,第一境界是会用,第二境界是理解为什么,第三境界是能扩展。所有的一切,例如做题,学习、看书、各种行为都是手段,为着我们更好的运用。因为,最终都是为了更好的去理解某个东西,以便于更好的去改造这个世界。理解世界,而不是解释世界;改造世界,而不是跟从世界。

目录

学习语言,第一境界是会用,第二境界是理解为什么,第三境界是能扩展。所有的一切,例如做题,学习、看书、各种行为都是手段,为着我们更好的运用。因为,最终都是为了更好的去理解某个东西,以便于更好的去改造这个世界。理解世界,而不是解释世界;改造世界,而不是跟从世界。

一、vector的介绍以及常用接口

1、成员函数

2、迭代器

a、下标方括号

b、迭代器

c、范围for(替换为迭代器)

3、Capacity

1、reserve

2、resize

3、shrink_to_fit 

4、内存管理

二、底层原理

1、push_back:

2、empty

3、insert

4、erase

5、resize

6、迭代器

7、函数寻址错误

8、隐式类型转换

9、string不能用memcpy进行拷贝

10、迭代器失效

11、算法库的find函数

12、vector二维数组

三、底层模拟代码


vector - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/vector/vector/?kw=vector


一、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);
        
     

    }



}


 

  • 43
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二十5画生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值