C++STL详解(二)vector的使用及其模拟实现

1.简介

vector官方文档

C++ STL中的verctor好比是C语言中的数组,但是vector又具有数组没有的一些高级功能。
在这里插入图片描述

  1. vector是可变大小的顺序容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好

2.常用接口

构造函数

构造函数文档

image-20220808173620723

alloc是空间配置器,暂时先不用理会。

如果vector的元素类型是int,默认初始化为0;如果vector元素类型为string,则默认初始化为空字符串。

vector<int> v1;
vector<string> v3;
vector<vector<int>>;  //这里相当于二维数组int a[n][n];
vector<int> v5 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
vector<string> v6 = { "hi","my","name","is","lee" };
vector<int> v7(5, -1); //初始化为-1,-1,-1,-1,-1。第一个参数是数目,第二个参数是要初始化的值
vector<string> v8(3, "hi");
vector<int> v9(10); //默认初始化为0
vector<string> v10(4); //默认初始化 为空字符串

利用迭代器区间初始化:

image-20220721201216830

void test_vector1()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	vector<double> v2;
	v2.push_back(1.1);
	v2.push_back(2.2);
	v2.push_back(3.3);

	vector<string> v3;
	v3.push_back("李白");
	v3.push_back("杜甫");
	v3.push_back("苏轼");
	v3.push_back("白居易");

	vector<int> v4(10, 5); // 10个5初始化v4

	vector<string> v5(++v3.begin(), --v3.end());
    
	string s = "hello world";
	vector<char> v6(s.begin(), s.end());
}

遍历

void test_vector2()
{
	// 遍历
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	// 1、下标+[]
	for (size_t i = 0; i < v.size(); ++i)
	{
		v[i] += 1;
		cout << v[i] << " ";
	}
	cout << endl;

	// 2.迭代器
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		*it -= 1;
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// 3.范围for
	for (auto& e : v)
	{
		e += 1;
		cout << e << " ";
	}
	cout << endl;
}

遍历二维的:

image-20220722103603875

void test_vector9()
{
	vector<vector<int>> vv;
	for (size_t i = 0; i < vv.size(); ++i)
	{
		for (size_t j = 0; j < vv[i].size(); ++j)
		{
			cout << vv[i][j] << " ";
		}
		cout << endl;
	}
}

在这里插入图片描述

扩容机制

// 查看扩容情况
void test_vector4()
{
	size_t sz;
	std::vector<int> foo;
	//foo.reserve(100); // 如果预知到大概插入多少数据利用reserve开好足够的空间
	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';
		}
	}
}

image-20220721203301998

Linux下g++版本:

在这里插入图片描述

  • VS用的STL是PJ版本,大约1.5倍扩容
  • Linux下用的是SGI版本,大约2倍扩容

单次增容越多,插入N个值,增容次数越少,效率就越高,但是带来的空间浪费可能也就越多。

单次增容越少,空间浪费较少,但增容次数增多,降低效率。

resize开空间+初始化。

  1. 当所给值大于容器当前的size时,将size扩大到该值,扩大的元素为第二个所给值,若未给出,则默认为0。
  2. 当所给值小于容器当前的size时,将size缩小到该值。
void test_vector5()
{
	vector<int> countV;
	countV.resize(10, 1); // 10个1
	for (auto e : countV)
	{
		cout << e << " ";
	}
	cout << endl;

	countV.resize(5); // 只保留下5个1
	for (auto e : countV)
	{
		cout << e << " ";
	}
	cout << endl;

	countV.resize(8, 10); // 5个1后面接上3个10
	for (auto e : countV)
	{
		cout << e << " ";
	}
	cout << endl;

	countV.resize(12); // 最后补上4个0凑足12
	for (auto e : countV)
	{
		cout << e << " ";
	}
	cout << endl;
}

image-20220721205744898

缩容

一般来说,删除数据是不会主动缩容的,缩容代价也比较大。
操作系统就不接受归还一部分内存的,只能整体归还。因此缩容也就变成把要保留的内容拷贝到一个合适的新空间,再把旧的整个空间释放,还给操作系统。

image-20220721210346140

void test_vector6()
{
	std::vector<int> foo;
	foo.reserve(100);
	foo.resize(10); //原来100个空间
	cout << foo.size() << endl;
	cout << foo.capacity() << endl;
	foo.shrink_to_fit(); // 把capacity缩到和size一样大
	cout << foo.size() << endl;
	cout << foo.capacity() << endl;
}

image-20220721210640011

shrink_to_fit()

image-20220721210822826

这个函数慎用,一般来说现代计算机内存是比较充足的。

插入删除

image-20220808175252013

image-20220808175217314

void test_vector7()
{
	// 遍历
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	v.insert(v.begin(), -1);
	v.insert(v.begin(), -2);
	v.insert(v.begin(), -3);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	v.insert(v.begin() + 7, 300); // 在任意位置插入都行
	//v.insert(v.begin()+8, 300); //越界了 err
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	v.erase(v.begin());
	v.erase(v.begin());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	v.erase(v.begin() + 1, v.begin() + 4);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

find

find文档

vector是没有实现find的,但如果我们非要找某个位置的话,可以用算法里的find()

image-20220721230656311

注意,STL中的迭代器基本都是左闭右开区间。

// find example
#include <iostream>     // std::cout
#include <algorithm>    // std::find
#include <vector>       // std::vector

int main () {
    // using std::find with array and pointer:
    int myints[] = { 10, 20, 30, 40 };
    int * p;

    p = std::find (myints, myints+4, 30);
    if (p != myints+4)
        std::cout << "Element found in myints: " << *p << '\n'; //30
    else
        std::cout << "Element not found in myints\n";

    // using std::find with vector and iterator:
    std::vector<int> myvector (myints,myints+4);
    std::vector<int>::iterator it = find (myvector.begin(), myvector.end(), 30);
    // 也可以用auto替换 std::vector<int>::iterator
    if (it != myvector.end())
        std::cout << "Element found in myvector: " << *it << '\n'; //30
    else
        std::cout << "Element not found in myvector\n";

    return 0;
}

sort

在这里插入图片描述

注意:默认是ascending order

// sort algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::sort
#include <vector>       // std::vector

bool myfunction (int i,int j) { return (i<j); }

struct myclass {
    bool operator() (int i,int j) { return (i<j);}
} myobject;

int main () {
    int myints[] = {32,71,12,45,26,80,53,33};
    std::vector<int> myvector (myints, myints+8);               // 32 71 12 45 26 80 53 33

    // using default comparison (operator <):
    std::sort (myvector.begin(), myvector.begin()+4);           //(12 32 45 71)26 80 53 33

    // using function as comp
    std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80)

    // using object as comp
    std::sort (myvector.begin(), myvector.end(), myobject);     //(12 26 32 33 45 53 71 80)

    // print out content:
    std::cout << "myvector contains:";
    for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
        std::cout << ' ' << *it;
    std::cout << '\n';

    return 0;
}
void test_vector8()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(6);
	v.push_back(3);
	v.push_back(9);
	v.push_back(2);
	v.push_back(0);
	v.push_back(8);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//排升序
	sort(v.begin(), v.end()); // 默认是小于,就是升序
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	// 排降序 利用仿函数
	// 构造了一个匿名对象 
	//greater<int> g;
	//sort(v.begin(), v.end(), g); // 这样也行
	sort(v.begin(), v.end(), greater<int>()); // > 就是降序
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

image-20220721233621890

3.vector模拟实现

SGI版本源码

stl_vector.h

欣赏一下核心框架:
image-20220723161939061

在这里插入图片描述

构造函数

vector()
    :_start(nullptr)
        , _finish(nullptr)
        , _endofstorage(nullptr)
    {}

template<class InputIterator>
    vector(InputIterator first, InputIterator last)
    : _start(nullptr)
        , _finish(nullptr)
        , _endofstorage(nullptr)
    {
        while (first != last)
        {
            push_back(*first);
            ++first;
        }
    }
void test_yzq_vector6()
{
    std::string s("hello");
    yzq::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    yzq::vector<int> v1(v.begin(), v.end());
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    yzq::vector<char> v2(s.begin(), s.end());
    for (auto e : v2)
    {
        cout << e << " ";
    }
    cout << endl;
}

再写一个:

vector(size_t n, const T& val = T())
    : _start(nullptr)
        , _finish(nullptr)
        , _endofstorage(nullptr)
    {
        reserve(n);
        for (size_t i = 0; i < n; ++i)
        {
            push_back(val);
        }
    }
void test_yzq_vector8()
{
    yzq::vector<int> v(10, 2);
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
}

怎么突然编译错误了呢?

image-20220724102933892

一点一点屏蔽代码,发现错误出现在调用构造函数那一行。

再测一组数据,又能正常使用了?
image-20220724103141374

在这里插入图片描述

看一下SGI中是咋写的:
在这里插入图片描述

再增加一个重载,就能匹配对了。

vector(int n, const T& val = T())
    : _start(nullptr)
        , _finish(nullptr)
        , _endofstorage(nullptr)
    {
        reserve(n);
        for (int i = 0; i < n; ++i)
        {
            push_back(val);
        }
    }

image-20220724104646772

深拷贝

现代写法。

void swap(vector<T>& v)
{
    std::swap(_start, v._start);
    std::swap(_finish, v._finish);
    std::swap(_endofstorage, v._endofstorage);
}

vector(const vector<T>& v)
    : _start(nullptr)
        , _finish(nullptr)
        , _endofstorage(nullptr)
    {
        vector<T> tmp(v.begin(), v.end());
        swap(tmp);
        //this->swap(tmp); 等价
    }

vector<T>& operator=(vector<T> v)
{
    this->swap(v);
    return *this;
}

void test_yzq_vector7()
{
    std::string s("hello");
    yzq::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    yzq::vector<int> v1(v);
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    yzq::vector<int> v2 = v;
    for (auto e : v2)
    {
        cout << e << " ";
    }
    cout << endl;
}

注意:

image-20220724102039540

在类里面定义的时候,不加上<T>也是可以的,也有人这么写,但是极其不推荐这么做,比较怪异。
统一加上模板参数才好。

push_back

void push_back(const T& x)
{
    if (_finish == _endofstorage)
    {
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
    }

    *_finish = x;
    ++_finish;
}

void pop_back()
{
    if (_finish > _start)
    {
        --_finish;
    }
}

reserve

注意需要先保存一下size的值,中间涉及到strat的改变。

void reserve(size_t n)
{
    size_t sz = size();
    // 不考虑缩容的问题
    if (n > capacity())
    {
        T* tmp = new T[n];
        if (_start) // 有数据才拷贝
        {
            memcpy(tmp, _start, size() * sizeof(T)); //这里是浅拷贝
            delete[] _start;
        }
        _start = tmp;
    }

    //_finish = _start + size(); // size()的计算出现了错误,_start已经改变了
    //_endofstorage = _start + capacity();
    _finish = _start + sz;
    _endofstorage = _start + n;
}

resize

C++中的内置类型也有有构造函数、析构函数的,这样才能更好的配合模板使用。

image-20220723162033781

// void resize(size_t n, T val = T()) T()是生成匿名对象,调用默认构造函数
// 注意:C++的内置类型也是有默认构造函数的 int的就是0 double的就是0.0
void resize(size_t n, const T& val = T())
{
    // 3种情况
    // 大于capacity
    if (n > capacity())
    {
        reserve(n);
    }
    // 大于size小于capacity
    if (n > size())
    {
        while (_finish < _start + n)
        {
            *_finish = val;
            ++_finish;
        }
    }
    else // 小于size
    {
        _finish = _start + n;
    }
}
void test_yzq_vector2()
{
	yzq::vector<int> v1;
	// 更多的还是利用resize完成初始化
	v1.resize(15, -2);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

insert

image-20220723165544390

void insert(iterator pos, const T& x)
{
    assert(pos >= _start && pos <= _finish);
    // 考虑扩容
    if (_finish == _endofstorage)
    {
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
    }

    // 挪动数据
    iterator end = _finish - 1;
    while (end >= pos)
    {
        *(end + 1) = *end;
        --end;
    }
    *pos = x;
    ++_finish;
}

一顿操作猛如虎,一下就写好了,测试一下。

在这里插入图片描述

感觉没毛病。

image-20220723164253324

欸,这是怎么回事?这就涉及迭代器失效的问题了。

迭代器失效

  1. 野指针失效
  2. 迭代器指向的意义变了

image-20220723170110128

image-20220723170118747

void insert(iterator pos, const T& x)
{
    assert(pos >= _start && pos <= _finish);
    // 考虑扩容
    // 扩容后pos失效了,利用相对距离更新pos
    if (_finish == _endofstorage)
    {
        size_t n = pos - _start;
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
        pos = _start + n;
    }

    // 挪动数据
    iterator end = _finish - 1;
    while (end >= pos)
    {
        *(end + 1) = *end;
        --end;
    }
    *pos = x;
    ++_finish;
}

再举个例子👇

void test_yzq_vector4()
{
	// 在所有的偶数前面插入一个20
	yzq::vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);
	v1.push_back(6);
	yzq::vector<int>::iterator it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
		{
			v1.insert(it, 20);
		}
		++it;
	}
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

image-20220723170934864

调试一下,发现确实已经完成了插入。

image-20220723171802823

但是我们insert里面不是完成了对pos的更新吗,为啥外面的it还是失效了?

因为这是值传递,形参的改变不影响实参。

那我们引用传递?不行的,加了引用,v1.begin()返回的临时对象(具有常属性)就传不过来了。
image-20220723172146600

即使用reserve开辟足够的空间,不会发生扩容,it没有变成野指针,但是也存在问题。

image-20220723172551112

尽管it++了,但指向的内容还是2,因为还在it的前面也插入了20。
it指向位置的意义发生变化,导致重复插入20,这也是迭代器失效

解决办法:

Return value:
An iterator that points to the first of the newly inserted elements.

iterator insert(iterator pos, const T& x)
{
    assert(pos >= _start && pos <= _finish);
    // 考虑扩容
    // 扩容后pos失效了,利用相对距离更新pos
    if (_finish == _endofstorage)
    {
        size_t n = pos - _start;
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
        pos = _start + n;
    }

    // 挪动数据
    iterator end = _finish - 1;
    while (end >= pos)
    {
        *(end + 1) = *end;
        --end;
    }
    *pos = x;
    ++_finish;

    return pos;
}
void test_yzq_vector4()
{
    // 在所有的偶数前面插入一个20
    yzq::vector<int> v1;
    //v1.reserve(20);
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(5);
    v1.push_back(6);
    yzq::vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        if (*it % 2 == 0)
        {
            it = v1.insert(it, 20);
            ++it;
        }
        ++it;
    }
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
}

image-20220723173439300

不同平台对比:

看起来在这一点上,MSVC检查比g++更严格。
VS能检查出野指针问题。

image-20220723210024890

erase

一般vector删除数据,都不考虑缩容的方案。

缩容方案:
size < 1/2capacity 时,可以考虑 开辟小一点新空间,拷贝过去,释放旧空间。
缩容方案是用时间来换空间,不太推荐。
实际中比较关注时间效率,现代的空间存储成本相对比较便宜。

Return value:
An iterator pointing to the new location of the element that followed the last element

// 要考虑到实现者有可能对erase实现缩容,而缩容开辟新空间会改变pos位置
// 因此需要返回迭代器
iterator erase(iterator pos)
{
    // 1 2 3 4
    // 1 3 4
    assert(pos >= _start && pos < _finish);
    iterator it = pos + 1;
    while (it != _finish)
    {
        *(it - 1) = *it;
        ++it;
    }
    --_finish;
    return pos;
}

void test_yzq_vector5()
{
    yzq::vector<int> v1;
    //v1.reserve(10);
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    cout << v1.size() << " " << v1.capacity() << endl;
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    auto pos = find(v1.begin(), v1.end(), 2);
    if (pos != v1.end())
    {
        v1.erase(pos);
    }
    cout << *pos << endl;
    *pos = 10;
    cout << *pos << endl;
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << v1.size() << " " << v1.capacity() << endl;
}

erase的失效基本都是意义变了或者不再访问有效数据,除非实现erase时缩容了,才有可能出现野指针的迭代器失效。

erase(pos)以后pos就是失效的了,因为pos的意义变了。

VS会对erase的意义改变造成的失效进行强制检查,报断言错误。
而Linux下就不会检查这种迭代器失效。

再来个例子:删除偶数

void test_vs_vector5()
{
    std::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(2);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    cout << v1.size() << " " << v1.capacity() << endl;
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    auto it = v1.begin();
    while (it != v1.end())
    {
        if (*it % 2 == 0)
        {
            v1.erase(it);
        }
            ++it;
    }
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
}

在这里插入图片描述

VS、Linux下均会出现错误。

正确代码:

void test_vs_vector5()
{
    std::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(2);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    cout << v1.size() << " " << v1.capacity() << endl;
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    auto it = v1.begin();
    while (it != v1.end())
    {
        if (*it % 2 == 0)
        {
            it = v1.erase(it);
        }
        else
        {
            ++it;
        }
    }
    for (auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << v1.size() << " " << v1.capacity() << endl;
}

image-20220724003228968

总结:

erase(pos)以后,pos失效了,pos的意义发生改变,但是在不同平台下面对于访问pos反应不同。
我们用的时候一定要小心,统一以失效的角度去看待。

对于inserterase造成的迭代器失效问题:
Linux g++平台检查佛系,基本只是依靠操作系统自身的野指针越界检查机制。
Windows下的VS系列检查更加严格,使用了一些强制检查机制,即使只是意义变了也可能会检查出来。

string也是有迭代器失效的问题的,但我们用string的insert、erase基本都是用的下标版本,因此也就不关心迭代器失效问题。

更深层次深拷贝

先用std试一下:

class Solution3 {
    public:
    std::vector<vector<int>> generate(int numRows) {
        std::vector<vector<int>> vv;
        vv.resize(numRows); // 设置总共的行数,默认都初始化为0
        for (size_t i = 0; i < vv.size(); ++i)
        {
            // 每一行i+1个数
            vv[i].resize(i + 1);
            // vv[i][0] = 1, vv[i][vv[i].size()-1] = 1;
            vv[i].front() = 1, vv[i].back() = 1; //利用函数也行
        }

        for (size_t i = 0; i < vv.size(); ++i)
        {
            for (size_t j = 0; j < vv[i].size(); ++j)
            {
                if (vv[i][j] == 0)
                {
                    vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
                }
            }
        }

        return vv;
    }
};

void test_yzq_vector9()
{
    std::vector<vector<int>> vv = Solution3().generate(5);
    for (size_t i = 0; i < vv.size(); ++i)
    {
        for (size_t j = 0; j < vv[i].size(); ++j)
        {
            cout << vv[i][j] << " ";
        }
        cout << endl;
    }
}

image-20220724105520790

再换成我们自己写的:
image-20220724105613220
出现了死循环。

通过验证发现,函数内部是没有问题,问题出在return上面。
image-20220724111041309
这是一个传值返回,会进行一次拷贝构造,再进行构造,但被编译器优化以后,直接进行构造了。
在这里插入图片描述

总结:
vector<T>中,当T是涉及深拷贝的类型,如string、vector<T>等类型时,扩容使用的memcpy拷贝数据是存在浅拷贝问题的。

解决办法:
不要用memcpy一个一个拷贝数据就行。
在这里插入图片描述

image-20220724114044045
在这里插入图片描述

源代码

#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <functional>
#include <assert.h>
using namespace std;

namespace yzq
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{}

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(size_t n, const T& val = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}

		vector(int n, const T& val = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}

		vector(const vector<T>& v)
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
			//this->swap(tmp); 等价
		}

		vector<T>& operator=(vector<T> v)
		{
			this->swap(v);
			return *this;
		}

		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endofstorage = nullptr;
			}
		}

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
			return _endofstorage - _start;
		}

		void reserve(size_t n)
		{
			size_t sz = size();
			// 不考虑缩容的问题
			if (n > capacity())
			{
				T* tmp = new T[n];
				if (_start) // 有数据才拷贝
				{
					//mcpy(tmp, _start, size() * sizeof(T)); 存在浅拷贝问题 要一个一个拷贝
					for (size_t i = 0; i < size(); ++i)
					{
						tmp[i] = _start[i]; // 如果涉及深拷贝,会去调用自己的赋值重载完成深拷贝
					}
					delete[] _start;
				}
				_start = tmp;
			}

			//_finish = _start + size(); // size()的计算出现了错误,_start已经改变了
			//_endofstorage = _start + capacity();
			_finish = _start + sz;
			_endofstorage = _start + n;
		}

		//void resize(size_t n, T val = T()) T()是生成匿名对象,调用默认构造函数
		// 注意:C++的内置类型也是有默认构造函数的 int的就是0 double的就是0.0
		void resize(size_t n, const T& val = T())
		{
			// 3种情况
			// 大于capacity
			if (n > capacity())
			{
				reserve(n);
			}
			// 大于size小于capacity
			if (n > size())
			{
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
			else // 小于size
			{
				_finish = _start + n;
			}
		}

		// 传引用减少开销,不改变就加const
		void push_back(const T& x)
		{
			/*
			if (_finish == _endofstorage)
			{
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
			}

			*_finish = x;
			++_finish;
			*/
			// 复用insert
			insert(end(), x);
		}

		void pop_back()
		{
			/*
			if (_finish > _start)
			{
				--_finish;
			}
			*/
			//复用erase
			erase(end() - 1);
			// 不能--end() end返回的临时对象具有常性
		}

		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos) const
		{
			assert(pos < size());
			return _start[pos];
		}

		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);
			// 考虑扩容
			// 扩容后pos失效了,利用相对距离更新pos
			if (_finish == _endofstorage)
			{
				size_t n = pos - _start;
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
				pos = _start + n;
			}

			// 挪动数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;

			return pos;
		}

		// 要考虑到实现者有可能对erase实现缩容,而缩容开辟新空间会改变pos位置
		// 因此需要返回迭代器
		iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);
			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			return pos;
		}

		void clear()
		{
			_finish = _start;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

尾声

🌹🌹🌹

写文不易,如果有帮助烦请点个赞~ 👍👍👍

Thanks♪(・ω・)ノ🌹🌹🌹

😘😘😘

👀👀由于笔者水平有限,在今后的博文中难免会出现错误之处,本人非常希望您如果发现错误,恳请留言批评斧正,希望和大家一起学习,一起进步ヽ( ̄ω ̄( ̄ω ̄〃)ゝ,期待您的留言评论。
附GitHub仓库链接

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值