C++中的vector类模拟实现

这篇具有很好参考价值的文章主要介绍了C++中的vector类模拟实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

vector模拟实现

vector类设计

vector类构造函数

vector类根据个数构造函数

vector类根据迭代器区间构造函数

vector类拷贝构造函数

vector类赋值运算符重载函数

vector类析构函数

vector类获取有效数据个数函数

vector类获取容量大小函数

vector类begin()函数

vector类end()函数

vector类reserve()函数

vector类push_back()函数

vector类empty()函数

vector类pop_back()函数

vector类resize()函数

vector类operator[]()重载函数

vector类insert()函数

vector类erase()函数

vector类swap()函数


vector模拟实现

因为vector类是一种模版类,所以在设计vector类时需要考虑到使用模版,本次模拟实现参考SGI版本的源代码实现基础版本并结合一部分PJ版本的思路

vector类设计

#pragma once

#include <iostream>
#include <cassert>
using namespace std;

namespace sim_vector
{
    template<class T>
    class vector
    {
    public:
        typedef T* iterator;//将普通指针作为迭代器
        typedef const T* const_iterator;//将const指针作为const迭代器

        //无参构造函数
        vector()
            :_start(nullptr)
            ,_finish(nullptr)
            ,_end_of_storage(nullptr)
        {}

        //根据个数构造函数
        vector(size_t n, const T& val = T());

        //根据个数构造函数
        vector(int n, const T& val = T());

        //根据迭代器区间构造函数
        template<class InputIterator>
        vector(InputIterator first, InputIterator last);

        //拷贝构造函数
        vector(const vector<T>& v);
        
        //赋值运算符重载函数——内部交换思路
        vector<T>& operator=(vector<T> v);

        //swap()函数
        void swap(vector<T> v);

        //析构函数
        ~vector();

        //获取有效数据个数函数
        const size_t size() const;

        //获取容量大小函数
        const size_t capacity() const;

        //begin()函数_非const
        iterator begin();

        //begin()函数_const
        const_iterator begin() const;

        //end()函数_非const
        iterator end();

        //end()函数_const
        const_iterator end() const;

        //reserve()函数
        void reserve(size_t newCapacity);

        //push_back()函数
        void push_back(const T& val);

        //empty()函数
        bool empty();

        //pop_back()函数
        void pop_back();

        //resize()函数
        void resize(size_t newSize, T val = T());

        //operator[]()函数_非const
        T& operator[](const size_t pos);

        //operator[]()函数_const
        const T& operator[](const size_t pos) const;

        //insert()函数
        iterator insert(iterator pos, T val = T());

        //erase()函数
        iterator erase(iterator pos);
    private:
        iterator _start;
        iterator _finish;
        iterator _end_of_storage;
    };
}

vector类构造函数

设计vector类时主要使用指针来实现,所以构造函数中使所有指针均为空即可

//构造函数
vector()
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
{}

vector类根据个数构造函数

//根据个数构造函数
vector(size_t n, const T& val = T())
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
{
    for (size_t i = 0; i < n; i++)
    {
        push_back(val);
    }
}

vector类根据迭代器区间构造函数

为了满足vector类的迭代器区间不局限于本类的迭代器,所以需要用到函数模版

//根据迭代器区间构造函数
template<class InputIterator>
vector(InputIterator first, InputIterator last)
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
{
    iterator tmp = _start;
    //根据指定区间构造
    while (first != last)
    {
        push_back(*first);
        first++;
    }
}

有了迭代器区间的构造函数后,使用根据个数的构造函数将会出问题,下面是测试报错信息

测试代码如下:

void test_give()
{
    //使用迭代器区间构造vector
    string s = "hello world";
    sim_vector::vector<char> s1(s.begin(), s.end());
    for (size_t i = 0; i < s1.size(); i++)
    {
        cout << s1[i] << " ";
    }

    //根据个数构造函数
    sim_vector::vector<int> v(10, 1);
    for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
}

int main()
{
    test_give();

    return 0;
}
报错信息:
无法取消引用类型为“InputIterator”的操作数

上面的代码之所以会出现问题是因为当同时存在根据迭代器区间构造函数和根据个数的构造函数,若在调用时想使用根据个数的构造函数创建对象则编译器会默认匹配根据迭代器区间构造函数创建对象,而此时的迭代器参数值为整型常数,当解引用整型常数时就会出现问题

而编译器之所以会优先匹配迭代器区间而非个数是因为个数的构造函数的第一个参数为size_t类,而整型字面量默认是int类型,所以存在隐式类型转换,为了避免类型转换,编译器选择了模版迭代器函数

解决方案为:重载一个参数类型为int的根据个数的构造函数

//根据个数构造函数
vector(int n, const T& val = T())
    :_start(nullptr)
    , _finish(nullptr)
    , _end_of_storage(nullptr)
{
    for (int i = 0; i < n; i++)
    {
        push_back(val);
    }
}

vector类拷贝构造函数

设计拷贝构造函数时,需要将每个指针指向位置的值给新的对象,思路如下:

C++中的vector类模拟实现,C++基础知识,c++

下面是示例代码:

//拷贝构造函数
vector(const vector<T>& v)
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
{
    //为新对象开辟空间
    reserve(v.capacity());
    //拷贝数据
    memcpy(_start, v._start, sizeof(T) * v.size());
    //更改指针指向
    _finish = _start + v.size();
}

上面的拷贝构造函数测试如下:

void test_copy()
{
    //使用拷贝构造
    sim_vector::vector<int> v(10, 1);
    sim_vector::vector<int> v1(v);

    for (auto num : v1)
    {
        cout << num << " ";
    }
}

int main()
{
    test_copy();
    
    return 0;
}
输出结果:
1 1 1 1 1 1 1 1 1 1

根据上面的测试貌似没有任何问题,但是下面的测试将会出现问题

测试代码如下:

void test_copy()
{
    //使用拷贝构造_自定义类型
    sim_vector::vector<string> v(3, "111111");
    sim_vector::vector<string> v1(v);

    for (auto num : v1)
    {
        cout << num << " ";
    }
}

int main()
{
    test_copy();
    
    return 0;
}

上面的代码导致程序崩溃,具体原因为当vector的元素类型为涉及到资源分配的自定义类型时,析构函数对同一个空间进行了两次析构,因为第一次析构时已经将原空间释放,第二次析构就会出错,具体分析如下:

C++中的vector类模拟实现,C++基础知识,c++

创建v对象时,会创建3个string类对象,使用下标遍历访问可以通过地址找到指定的string对象,例如第一个string类对象

C++中的vector类模拟实现,C++基础知识,c++

如果直接使用上面的拷贝方式,拷贝过程中就会直接按照字节拷贝给v1对象,此时v1对象中的string类对象与v对象中的string类对象相同,因为vv1中的string类对象共用一块内存空间,所以在最后释放过程中,因为先释放v1对象,释放的过程中会依次先释放每一个string类对象,最后再释放v1对象的空间,而此时当v对象释放时,先释放string类对象,因为v1释放过程中已经释放了string类对象,v对象二次释放导致程序崩溃

📌

数组元素地址不等于元素本身的地址

所以可以考虑使用深拷贝的解决方案:

//拷贝构造函数
vector(const vector<T>& v)
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
{
    //为新对象开辟空间
    reserve(v.capacity());
    //拷贝数据
    //memcpy(_start, v._start, sizeof(T) * v.size());
    for (size_t i = 0; i < v.size(); i++)
    {
        _start[i] = v._start[i];
    }
    //更改指针指向
    _finish = _start + v.size();
}

测试上面的代码会发现,这个代码已经可以满足当元素为string类时的拷贝构造,测试代码如下:

void test_copy()
{
    sim_vector::vector<string> v(3, "111111");
    sim_vector::vector<string> v1(v);

    for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
    cout << endl;
    for (size_t i = 0; i < v.size(); i++)
    {
        v1[i] = "222";
        cout << v1[i] << " ";
    }
    cout << endl;
    //不改变原来的内容
    for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
}

int main()
{
    test_copy();
    
    return 0;
}
输出结果:
111111 111111 111111
222 222 222
111111 111111 111111

如果模板参数为当前模拟实现的vector类时又会出现上面同样的问题,下面是库内的vector类的测试结果作为对比:

#include <vector>

//杨辉三角题目测试
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv(numRows);
        //初始化每一个vector<int>元素
        for (size_t i = 0; i < numRows; i++)
        {
            vv[i].resize(i + 1, 1);//将每一个vector<int>初始化为i+1个数组,并初始化为1
        }

        //构造杨辉三角
        for (size_t i = 2; i < numRows; i++)
        {
            for (size_t j = 1; j < i; j++)
            {
                vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
            }
        }
        return vv;
    }
};

int main()
{
    vector<vector<int>> vv = Solution().generate(3);
    vector<vector<int>> vv1(vv);
    for (size_t i = 0; i < vv1.size(); i++)
    {
        for (size_t j = 0; j < vv1[i].size(); j++) {
            cout << vv1[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}
输出结果:
1
1 1
1 2 1

当模板参数切换为模拟实现的类时:

//杨辉三角题目测试
class Solution {
public:
    sim_vector::vector<sim_vector::vector<int>> generate(int numRows) {
        sim_vector::vector<sim_vector::vector<int>> vv(numRows);
        //初始化每一个vector<int>元素
        for (size_t i = 0; i < numRows; i++)
        {
            vv[i].resize(i + 1, 1);//将每一个vector<int>初始化为i+1个数组,并初始化为1
        }

        //构造杨辉三角
        for (size_t i = 2; i < numRows; i++)
        {
            for (size_t j = 1; j < i; j++)
            {
                vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
            }
        }
        return vv;
    }
};

int main()
{
    sim_vector::vector<sim_vector::vector<int>> vv = Solution().generate(3);
    sim_vector::vector<sim_vector::vector<int>> vv1(vv);
    for (size_t i = 0; i < vv1.size(); i++)
    {
        for (size_t j = 0; j < vv1[i].size(); j++) {
            cout << vv1[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

使用上面的测试会发现,同样在析构时出现同一空间进行两次析构导致程序崩溃,下面的是原因分析

C++中的vector类模拟实现,C++基础知识,c++

由上图可以看出,即使模板参数类型为vector<int>,和模板参数类型为string类也没有什么区别,那么问题在何处

观察上面的拷贝构造函数的实现代码可以发现,_start[i] = v._start[i]涉及到一个赋值运算,而string类本身已经重载过赋值运算符,所以是深拷贝,但是模拟类还没有实现赋值运算符的重载,所以在这里赋值时实际上还是调用的编译器默认的赋值运算符重载函数,依旧是浅拷贝,导致拷贝的结果也就是新对象装着被拷贝对象的旧内容,所以需要实现赋值运算符重载函数

vector类赋值运算符重载函数

//赋值运算符重载函数——内部重新赋值思路
vector<T>& operator=(vector<T> v)
{
    if (this != &v)
    {
        reserve(v.capacity());
        for (size_t i = 0; i < v.size(); i++)
        {
            _start[i] = v._start[i];
        }
        //改变指针指向
        _finish = _start + v.size();
    }
    return *this;
}

//赋值运算符重载函数——内部交换思路
vector<T>& operator=(vector<T> v)
{
    if (this != &v)
    {
        swap(v);
    }
    return *this;
}

现在再对模拟实现的vector类进行相同的测试

//杨辉三角题目测试
class Solution {
public:
    sim_vector::vector<sim_vector::vector<int>> generate(int numRows) {
        sim_vector::vector<sim_vector::vector<int>> vv(numRows);
        //初始化每一个vector<int>元素
        for (size_t i = 0; i < numRows; i++)
        {
            vv[i].resize(i + 1, 1);//将每一个vector<int>初始化为i+1个数组,并初始化为1
        }

        //构造杨辉三角
        for (size_t i = 2; i < numRows; i++)
        {
            for (size_t j = 1; j < i; j++)
            {
                vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
            }
        }
        return vv;
    }
};

int main()
{
    sim_vector::vector<sim_vector::vector<int>> vv = Solution().generate(3);
    sim_vector::vector<sim_vector::vector<int>> vv1(vv);
    for (size_t i = 0; i < vv1.size(); i++)
    {
        for (size_t j = 0; j < vv1[i].size(); j++) {
            cout << vv1[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}
输出结果:
1
1 1
1 2 1

vector类析构函数

//析构函数
~vector()
{
    delete[] _start;
    _start = _finish = _end_of_storage = nullptr;
}

vector类获取有效数据个数函数

当两个指针指向同一个空间时,二者差值即为两个指针相差的数据个数

//获取有效数据个数函数
const size_t size() const
{
    return _finish - _start;
}

vector类获取容量大小函数

当两个指针指向同一个空间时,尾指针减头指针即为容量大小

//获取容量大小函数
const size_t capacity() const
{
    return _end_of_storage - start;
}

vector类begin()函数

对于非const的对象时,调用非constbegin()函数

对于const的对象时,调用constbegin()函数

//begin()函数_非const
iterator begin()
{
    return _start;
}

//begin()函数_const
const_iterator begin() const
{
    return _start;
}

vector类end()函数

对于非const的对象时,调用非constend()函数

对于const的对象时,调用constend()函数

//end()函数_非const
iterator end()
{
    return _finish;
}

//end()函数_const
const_iterator end() const
{
    return _finish;
}

vector类reserve()函数

设计reserve()函数时需要注意,因为基本的扩容思路是开辟新空间再释放原空间,此时会涉及到指针指向的位置改变,而如果_start指针和_finish指针不是指向同一块空间时,那么就无法计算出sizecapacity

下面是错误代码演示:

//reserve()函数
void reserve(size_t newCapacity)
{
    //如果capacity比当前的capacity小或者相等时不进行处理
    int before = capacity();
    if (newCapacity <= before)
    {
        return;
    }

    //进行扩容
    T* tmp = new T[newCapacity];
    //如果有数据时进行数据移动
    if (_start)
    {
        memcpy(tmp, _start, sizeof(T) * size());
        //释放原空间
        delete[] _start;
    }

    //改变原指针指向
    _start = tmp;
    _finish = _start + size();
    _end_of_storage = _start + newCapacity;
}
调用函数reserve(10)测试输出结果:
0
0
18446743418279207180
10

错误原因分析:

C++中的vector类模拟实现,C++基础知识,c++

通过上面的分析可以得出错误的原因主要是size()函数计算的值为随机值,导致_finish指针错误,最后在测试中调用size()函数计算错误

因为拷贝数据的过程中,每个数据的间隔值不变,所以再扩容之前先记录开始的size即可

正确代码如下

//reserve()函数
void reserve(size_t newCapacity)
{
    //如果capacity比当前的capacity小或者相等时不进行处理
    size_t beforeCapacity = capacity();
    if (newCapacity <= beforeCapacity)
    {
        return;
    }

    //进行扩容
    T* tmp = new T[newCapacity];
    //记录开始的size
    size_t beforeSize = size();
    //如果有数据时进行数据移动
    if (_start)
    {
        memcpy(tmp, _start, sizeof(T) * size());
        //释放原空间
        delete[] _start;
    }

    //改变原指针指向
    _start = tmp;
    _finish = _start + beforeSize;
    _end_of_storage = _start + newCapacity;
}

vector类push_back()函数

vector类中的数据尾插时需要注意是否需要扩容,即当前对象的_finish指针和_end_of_storage指针是否指向同一个位置,不需要扩容时直接插入数据后改变_finish指针即可

//push_back()函数
void push_back(const T& val)
{
    //判断是否需要扩容
    if (_finish == _end_of_storage)
    {
        size_t beforeCapacity = capacity();
        reserve(beforeCapacity == 0 ? 4 : beforeCapacity * 2);
    }

    //扩容完毕后插入数据
    (*_finish) = val;//改变指向位置的值
    ++_finish;
}

vector类empty()函数

_start指针和_finish指针均指向起始位置时说明空间中无数据

//empty()函数
bool empty()
{
    return _start == _finish;
}

vector类pop_back()函数

设计pop_back()函数需要注意如果空间中无数据时不可以执行删除

//pop_back()函数
void pop_back()
{
    //空间内无数据时不允许删除
    assert(!empty());

    --_finish;
}

vector类resize()函数

设计resize()函数思路和string类中的思路一致,分三种情况讨论即可

需要注意的是,设计初始化值时,不要直接使用值,而是使用匿名对象调用对应的构造函数,因为vector中存储的数据类型不一定时内置类型,也有可能是自定义类型

另外对于初始化来说,不能是简单的赋值行为,需要考虑到深拷贝问题

📌

内置类型也有默认无参构造函数,但是用得非常少,注意指针无法使用对应的构造函数

//resize()函数
void resize(size_t newSize, T val = T())
{
    //当newSize小于当前的size时删除数据
    if (newSize <= size())
    {
        _finish = _start + newSize;
        _end_of_storage = _finish;
        return;
    }

    //当newSize大于当前的size时扩容+初始化
    //当newSize大于当前的capacity时进行扩容
    if (newSize > capacity())
    {
        reserve(newSize);
    }

    //初始化
    while (_finish != (_start + newSize))
    {
        //初始化
        *_finish = val;
        _finish++;
    }
}

vector类operator[]()重载函数

返回指定位置的数据引用即可

对于非const的对象时,调用非constoperator[]()函数

对于const的对象时,调用constoperator[]()函数

//operator[]()函数_非const
T& operator[](const size_t pos)
{
    assert(pos >= 0);
    assert(pos < size());
    return *(_start + pos);
}

//operator[]()函数_const
const T& operator[](const size_t pos) const
{
    assert(pos >= 0);
    assert(pos < size());
    return *(_start + pos);
}

vector类insert()函数

对于insert()函数来说,插入数据的思路和string类的插入思路基本一致

//insert()函数
void insert(iterator pos, T val = T())
{
    //确保位置合理
    assert(pos >= _start);
    assert(pos <= _finish);

    //当空间不足时进行扩容
    if (_finish == _end_of_storage)
    {
        reserve(capacity() * 2);
    }

    //将pos位置及以后的数据向后挪动一个位置
    iterator tmp = _finish + 1;
    while (tmp >= pos)
    {
        *tmp = *(tmp - 1);
        tmp--;
    }

    //pos位置插入数据
    (*pos) = val;
    ++_finish;
}

上面的代码中,如果在插入时没有涉及到空间扩容时没有问题,但是如果一旦涉及到空间扩容则会导致pos为野指针,测试如下:

//插入时无空间扩容
void test_insert()
{
    //插入五个数据时会扩容
    sim_vector::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);

    //因为当前的capacity为8,足够插入一个数据,故插入过程不需要扩容
    sim_vector::vector<int>::iterator pos = std::find(v.begin(), v.end(), 2);
    v.insert(pos, 20);
    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;
}

int main()
{
    test_insert();

    return 0;
}
输出结果:
1 20 2 3 4 5

//插入过程中有空间扩容
void test_insert()
{
    //再插入一个数据时需要扩容到空间大小为8
    sim_vector::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    sim_vector::vector<int>::iterator pos = std::find(v.begin(), v.end(), 2);
    v.insert(pos, 20);
    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;
}

int main()
{
    test_insert();

    return 0;
}
输出结果:
1 2 3 4 -842150451

出现上面的错误原因是因为扩容导致pos指针指向的位置失效,具体分析如下:

C++中的vector类模拟实现,C++基础知识,c++

C++中的vector类模拟实现,C++基础知识,c++

修改方式如下:

//insert()函数
void insert(iterator pos, T val = T())
{
    //确保位置合理
    assert(pos >= _start);
    assert(pos <= _finish);

    //记录pos与_start的差值
    size_t gap = pos - _start;
    //当空间不足时进行扩容
    if (_finish == _end_of_storage)
    {
        reserve(capacity() * 2);
        //扩容时更新pos
        pos = _start + gap;
    }

    //将pos位置及以后的数据向后挪动一个位置
    iterator tmp = _finish + 1;
    while (tmp >= pos)
    {
        *tmp = *(tmp - 1);
        tmp--;
    }

    //pos位置插入数据
    (*pos) = val;
    ++_finish;
}

但是上面的代码还存在第二个问题,如果在调用insert()函数后,如果涉及到扩容,虽然insert()函数内部解决了因为扩容导致的pos位置失效,但是并没有改变调用处的pos,如果在调用之后想改变pos位置的值,此时也会出现非法访问,从而出现随机值

测试如下:

void test_insert()
{
    sim_vector::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    //v.push_back(5);

    sim_vector::vector<int>::iterator pos = std::find(v.begin(), v.end(), 2);
    v.insert(pos, 20);
    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;
    (*pos)++;
    for (auto num : v)
    {
        cout << num << " ";
    }
}

int main()
{
    test_insert();

    return 0;
}

insert()函数内部涉及到扩容时改变pos位置:

C++中的vector类模拟实现,C++基础知识,c++

但是在函数外部并没有改变pos的位置:

C++中的vector类模拟实现,C++基础知识,c++

所以在设计insert()函数时可以考虑返回pos位置

//insert()函数
iterator insert(iterator pos, T val = T())
{
    //确保位置合理
    assert(pos >= _start);
    assert(pos <= _finish);

    //记录pos与_start的差值
    size_t gap = pos - _start;
    //当空间不足时进行扩容
    if (_finish == _end_of_storage)
    {
        reserve(capacity() * 2);
        //扩容时更新pos
        pos = _start + gap;
    }

    //将pos位置及以后的数据向后挪动一个位置
    iterator tmp = _finish + 1;
    while (tmp >= pos)
    {
        *tmp = *(tmp - 1);
        tmp--;
    }

    //pos位置插入数据
    (*pos) = val;
    ++_finish;

    return pos;
}

📌

所以在使用insert()函数,需要考虑上面出现的两种问题,这两种问题统称为迭代器失效问题

vector类erase()函数

设计erase()函数时,和string类的思想基本一致,但vector类删除的是迭代器指向位置的数据

//erase()函数
void erase(iterator pos)
{
    //确保位置有效
    assert(pos >= _start);
    assert(pos < _finish);

    //将pos后的数据向pos位置移动
    iterator tmp = pos + 1;
    while (tmp < _finish)
    {
        *(tmp - 1) = *tmp;
        tmp++;
    }

    --_finish;
}

上面的代码中直接删除对象空间中的数据没有任何问题,但是根据理解来说,如果在调用函数之后还想改变pos位置的值时就会访问错误,得到的结果也是随机值,实际上面的操作结果为:

void test_erase()
{
    sim_vector::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    sim_vector::vector<int>::iterator pos = std::find(v.begin(), v.end(), 1);
    v.erase(pos);
    for (auto num : v)
    {
        cout << num << " ";
    }
    
    cout << endl;
    (*pos)++;
    for (auto num : v)
    {
        cout << num << " ";
    }
}

int main()
{
    test_erase();

    return 0;
}
输出结果:
2 3 4 
3 3 4

结果应该是报错,因为没有pos位置的数据1,但是改变了后一个数据的位置并且正常输出,对比库中的erase()函数测试结果:

#include <vector>

void test_erase()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    vector<int>::iterator pos = std::find(v.begin(), v.end(), 1);
    v.erase(pos);
    for (auto num : v)
    {
        cout << num << " ";
    }

    cout << endl;
    (*pos)++;
    for (auto num : v)
    {
        cout << num << " ";
    }
}

int main()
{
    test_erase();

    return 0;
}

C++中的vector类模拟实现,C++基础知识,c++

标准库中的erase()函数此时直接报错,和正常理解没有歧义

上面的对比涉及到了两个平台的实现方式,第一种没有报错的对应与Linux下的g++,第二种报错的对应于VS下的MSVC

所以在使用erase()函数时,也需要注意迭代器失效的问题,因为再使用一开始的pos不同的平台结果不一样,即结果未定义

解决方式为记录删除的数据的下一个数据的位置,为函数加上返回值文章来源地址https://www.toymoban.com/news/detail-849651.html

//erase()函数
iterator erase(iterator pos)
{
    //确保位置有效
    assert(pos >= _start);
    assert(pos < _finish);

    //将pos后的数据向pos位置移动
    iterator tmp = pos + 1;
    while (tmp < _finish)
    {
        *(tmp - 1) = *tmp;
        tmp++;
    }

    --_finish;

    return pos;
}

vector类swap()函数

//swap()函数
void swap(vector<T> v)
{
    std::swap(_start, v._start);
    std::swap(_finish, v._finish);
    std::swap(_end_of_storage, v._end_of_storage);
}

到了这里,关于C++中的vector类模拟实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++ 模拟实现vector

    目录 一、定义 二、模拟实现 1、无参初始化 2、sizecapacity 3、reserve 4、push_back 5、迭代器 6、empty 7、pop_back 8、operator[ ] 9、resize 10、insert 迭代器失效问题 11、erase 12、带参初始化 13、迭代器初始化 14、析构函数 15、深拷贝 16、赋值运算符重载 完整版代码测试代码 本次参考SGI版

    2024年02月04日
    浏览(30)
  • C++模拟实现vector

    目录 1.代码实现 2.注意事项 1.成员变量 2. 不能使用memcpy函数拷贝数据 1.用string类型测试时,要考虑到vs可能把数据存储在数组buffer里面 3.insert函数中指针的失效性 1.加引用,那么就不能传常量,比如v.begin() + 3 2.加引用,就只能传变量了  4.erase成员函数的指针的失效性 这边以

    2024年02月17日
    浏览(34)
  • 【C++】vector模拟实现

    🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🚁 个人主页:不 良 🔥 系列专栏:🛸C++  🛹Linux 📕 学习格言:博观而约取,厚积而薄发 🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸君一同

    2024年02月13日
    浏览(30)
  • [C++]:11.模拟实现vector

    1.vector.h vector.h中其实包含了许多的头文件,我们在cpp中包含文件的时候头文件中还会去展开这四个头文件关于vector类主要在这个stl_vector.h中。 2.stl_vector.h 1.构造: ps:在当前的学习阶段看源码不要一行一行去看因为水平不足所以非常多基本上是看不懂的所以不要去一行一行去

    2024年01月16日
    浏览(33)
  • STL 关于vector的细节,vector模拟实现【C++】

    _start指向容器的头,_finish指向容器当中 有效数据 的下一个位置,_endofstorage指向整个容器的尾 先开辟一块与该容器大小相同的空间,然后将该容器当中的数据一个个拷贝过来即可,最后更新_finish和_endofstorage的值即可。 深拷贝版本一: 注意: 不能使用memcpy函数 , 如果vec

    2024年02月15日
    浏览(30)
  • C++——vector类及其模拟实现

    前言:前边我们进行的string类的方法及其模拟实现的讲解。这篇文章将继续进行C++的另一个常用类——vector。 vector和string一样, 隶属于C++中STL标准模板库中的一个自定义数据类型 ,实际上就是 线性表 。 两者之间有着很多相似,甚至相同的方法 。 但是它们也有着很大的不

    2024年04月13日
    浏览(29)
  • C++ STL vector 模拟实现

    ✅1主页:我的代码爱吃辣 📃2知识讲解:C++之STL 🔥3创作者:我的代码爱吃辣 ☂️4开发环境:Visual Studio 2022 💬5前言:上次我们已经数字会用了vector,这次我们对其底层更深一步挖掘,其中重点是,Vector中一些深浅拷贝问题。 目录 一.Vector模拟实现的整体框架 二. Vector的构

    2024年02月13日
    浏览(25)
  • 【C++】vector模拟实现及其应用

    vector是表示可变大小数组的序列容器。 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。 本质讲,vector使用动态分

    2023年04月25日
    浏览(30)
  • C++:vector使用以及模拟实现

    和我们原来讲的string不同, vector并不是类,是一个类模板,加类型实例化以后才是类。 vector是表示 可变大小数组 的序列容器。 像数组一样 ,vector也采用的连续存储空间来存储元素,但是容量可以动态改变。 和其它容器相比,vector访问元素、尾插、尾删较高效,但不在尾部

    2024年02月11日
    浏览(35)
  • 【C++ STL】vector模拟实现

    2023年05月17日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包