【C++】8.vector

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 用三个指针来代替

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 完 

猜你喜欢

转载自blog.csdn.net/szh0331/article/details/129476010