Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++指南】vector(二):手把手教你底层原理与模拟实现

【C++指南】vector(二):手把手教你底层原理与模拟实现

原创
作者头像
倔强的石头_
发布于 2025-05-15 15:22:18
发布于 2025-05-15 15:22:18
17900
代码可运行
举报
文章被收录于专栏:C/C++指南C/C++指南
运行总次数:0
代码可运行

@toc

一、引言

在 C++ 标准库中,vector 是最常用的动态数组容器,它提供了高效的元素存储和访问能力。

其底层实现涉及内存管理、迭代器维护、元素操作等复杂逻辑。

本文将基于自主实现的 xc::vector 类,深入探讨其设计原理与关键功能实现

vector系列关联文章:【C++指南】vector(一):从入门到详解

二、成员变量

通过对stl库中vector的实现进行分析:

我们发现其成员变量与我们想象的不一致,并不是通过一个指针指向数据和两个size_t size和capacity

在这里插入图片描述
在这里插入图片描述

通过默认构造找到了其基于三个成员变量startfinishendofstorage来实现vector

并通过size和capacity成员函数的实现可以明白:==STL库是通过指针做差来计算size和capacity==

接着往下深挖,可以找到:

==三个成员变量的数据类型,最终是基于模板类型的指针==

在这里插入图片描述
在这里插入图片描述

于是,我们通过模拟STL库的方式来实现vector

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
private:

    iterator \_start = nullptr;    // 指向数据存储起始位置

    iterator \_finish = nullptr;   // 指向有效数据尾部的下一个位置

    iterator \_endofstorage = nullptr; // 指向存储容量尾部

通过三个指针实现动态数组的核心控制:

  • \_start:内存块的起始地址
  • \_finish:当前有效元素的末尾的下一个位置
  • \_endofstorage:内存块的总容量

三、默认成员函数

2.1 默认构造函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
vector()//默认构造

{}

**思路**:默认构造函数不进行任何初始化操作,将成员指针初始化为 nullptr,表示当前 vector 为空。

2.2 析构函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
~vector()

{

    delete[]\_start;

    \_start = \_finish = \_endofstorage = nullptr;

}

**思路**:析构函数负责释放 vector 所占用的内存。首先使用 delete[] 释放存储元素的数组,然后将三个成员指针都置为 nullptr,避免悬空指针。

2.3 拷贝构造函数

传统写法
代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
//vector(const vector<T>& v)//老实人写法

//{

//    size\_t capacity = v.capacity();//开空间和初始化

//    \_start = new T[capacity];

//    \_endofstorage = \_start + capacity;

//    \_finish = \_start + v.size();



//    for (size\_t i = 0; i < size(); i++)//拷贝数据

//    {

//        \_start[i] = v.\_start[i];

//    }

//}

**思路**:传统的拷贝构造函数首先根据源 vector 的容量分配新的内存空间,然后将源 vector 的元素逐个复制到新的内存空间中。最后更新 \_finish\_endofstorage 指针。

==注意==

拷贝数据不可用memcpy ,为了防止数据类型是自定义类型而引发的浅拷贝问题,而采用逐个位置赋值,这样就可以调用数据类型相应的构造函数了

在C++中所有容器相关的拷贝中,都要用深拷贝来代替浅拷贝

现代写法
代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
vector(const vector<T>& v)//偷懒式写法

{

    reserve(v.capacity());



    for (auto i = v.cbegin(); i != v.cend(); ++i)

    {

        push\_back(\*i);

    }

}

**思路**:现代写法利用 reserve 函数预先分配足够的内存空间,然后通过 push\_back 函数将源 vector 的元素逐个添加到新的 vector 中。这种写法更加简洁,并且利用了已有的 push\_back 函数,减少了代码的重复。

2.4 赋值重载函数

传统写法
代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
//vector<T>& operator=(const vector<int>& v)//老实人写法

//{

//    size\_t capacity = v.capacity();//开空间和初始化

//    \_start = new T[capacity];

//    \_endofstorage = \_start + capacity;

//    \_finish = \_start + v.size();



//    for (size\_t i = 0; i < size(); i++)//拷贝数据

//    {

//        \_start[i] = v.\_start[i];

//    }

//    return \*this;

//}

**思路**:传统的赋值重载函数首先释放当前 vector 所占用的内存,然后根据源 vector 的容量分配新的内存空间,将源 vector 的元素逐个复制到新的内存空间中。最后返回当前对象的引用。

现代写法
代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
vector<T>& operator=(vector<int>v)//偷懒式写法

{

    swap(v);

    return \*this;

}

**思路**:现代写法采用了“拷贝 - 交换”技术。首先通过值传递的方式接收一个临时对象 v,然后调用 swap 函数将当前对象和临时对象的成员指针进行交换。这样就完成了赋值操作,并且保证了异常安全性。最后返回当前对象的引用。

四、元素访问相关

3.1 [] 重载(非 const 版本)

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
T& operator[](size\_t i)

{

    return \_start[i];

}

**思路**:非 const 版本的 [] 重载函数返回指定位置元素的引用,允许对元素进行修改。

3.2 [] 重载(const 版本)

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
const T& operator[](size\_t i)const

{

    return \_start[i];

}

**思路**:const 版本的 [] 重载函数返回指定位置元素的 const 引用,用于在 const 对象上访问元素,不允许对元素进行修改。

五、迭代器相关

4.1 迭代器类型声明

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
typedef T\* iterator;

typedef const T\* const\_iterator;

**思路**:定义了两种迭代器类型,iterator 用于非 const 对象的迭代,const\_iterator 用于 const 对象的迭代。

4.2 begin 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
iterator begin()

{

    return \_start;

}

**思路**:begin 函数返回指向 vector 第一个元素的迭代器。

4.3 end 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
iterator end()

{

    return \_finish;

}

**思路**:end 函数返回指向 vector 最后一个元素的下一个位置的迭代器。

4.4 cbegin 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
const\_iterator cbegin() const

{

    return \_start;

}

**思路**:cbegin 函数返回指向 vector 第一个元素的 const 迭代器,用于在 const 对象上迭代。

4.5 cend 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
const\_iterator cend() const

{

    return \_finish;

}

**思路**:cend 函数返回指向 vector 最后一个元素的下一个位置的 const 迭代器,用于在 const 对象上迭代。

六、容量相关函数

5.1 size 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
size\_t size()const

{

    return \_finish - \_start;

}

**思路**:size 函数返回 vector 中当前元素的数量,通过 \_finish\_start 指针的差值计算得到。

5.2 capacity 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
size\_t capacity()const

{

    return \_endofstorage - \_start;

}

**思路**:capacity 函数返回 vector 当前分配的内存空间能够容纳的元素数量,通过 \_endofstorage\_start 指针的差值计算得到。

5.3 empty 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
bool empty()

{

    return \_start == \_finish;

}

**思路**:empty 函数判断 vector 是否为空,通过比较 \_start\_finish 指针是否相等来实现。

5.4 resize 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
void resize(size\_t n,const T& val=T())

{

    if (n <= size())

    {

        \_finish = \_start + n;

    }

    else

    {

        reserve(n);//该函数只有n大于capacity才会扩容

        for (size\_t i = size(); i < n; ++i)

        {

            push\_back(val);

        }

    }

}

**思路**:resize 函数用于调整 vector 的大小。如果 n 小于等于当前大小,则将 \_finish 指针移动到 \_start + n 的位置;如果 n 大于当前大小,则先调用 reserve 函数确保有足够的容量,然后使用 push\_back 函数将元素添加到 vector 中,直到大小达到 n

==可参考下面两张图来理解resizeu对于不同的n所采取的不同处理方式==

当n小于等于size时

在这里插入图片描述
在这里插入图片描述

当n大于size时

包括两种情况,分别是size<n<capacity,以及n>capacity

两种处理结果稍有不同

在这里插入图片描述
在这里插入图片描述

5.5 reserve 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
void reserve(size\_t n)

{

    if (n > capacity())

    {



        size\_t size = \_finish - \_start;//提前存下size防止迭代器失效

        T\* tmp = new T[n];



        if (\_start)//如果原空间不为空,则拷贝数据

        {

            for (size\_t i = 0; i < size; ++i)

            {

                tmp[i] = \_start[i];

            }

        }

        

        delete[]\_start;

        \_start = tmp;

        \_finish = \_start + size;

        \_endofstorage = \_start + n;

    }

}

**思路**:reserve 函数用于增加 vector 的容量。如果 n 大于当前容量,则分配一个新的内存空间,将原有的元素复制到新的内存空间中,然后释放原有的内存空间。在操作过程中,需要提前记录当前的大小,以避免迭代器失效。

==reserve、insert和erase的实现,涉及到迭代器失效的问题,限于本篇文章主题和篇幅限制,没法在此展开,故放到系列第三篇文章,单独来讲解==

如果对代码有不理解的地方可以直接点击链接跳转下一篇文章:

七、修改相关操作

6.1 push\_back 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
void push\_back(const T& val)  //尾插

{

    if (\_finish == \_endofstorage)

    {

        reserve(capacity() == 0 ? 4 : 2 \* capacity());

    }

    \*(\_finish++) = val;



}

**思路**:push\_back 函数用于在 vector 的末尾添加一个元素。如果当前容量不足,则调用 reserve 函数进行扩容,然后将元素赋值给 \_finish 指针所指向的位置,并将 \_finish 指针向后移动一位。

6.2 pop\_back 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
void pop\_back() //尾删

{

    assert(!empty());

    --\_finish;

}

**思路**:pop\_back 函数用于删除 vector 的最后一个元素。在删除之前,需要确保 vector 不为空,然后将 \_finish 指针向前移动一位。

6.3 insert 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
void insert(iterator pos, const T& val) //插入  

{

    assert(pos >= begin()); //位置合法性判断

    assert(pos <= end());



    

    if (\_finish == \_endofstorage)//判断扩容

    {

        size\_t len = pos - begin();//提前存下pos相对位置,防止pos迭代器失效

        reserve(capacity() == 0 ? 4 : 2 \* capacity());

        pos = begin() + len;



    }



    iterator i = end();//挪动数据

    while (i != pos) 

    {

        \*i = \*(i - 1);

        --i;

    }



    \*pos = val;

    ++\_finish;

}

**思路**:insert 函数用于在指定位置插入一个元素。首先进行位置合法性检查,如果当前容量不足,则进行扩容操作。在扩容过程中,需要提前记录插入位置的相对位置,以避免迭代器失效。然后将插入位置之后的元素依次向后移动一位,最后将元素插入到指定位置,并将 \_finish 指针向后移动一位。

==可参考下面两张图来理解insert的过程==

1.首先判断是否需要扩容

2.然后需要判断和挪动数据(因为vector底层是基于顺序表的原理实现的)

3.最后将空出来的位置插入数据 ,修改finish的值

在这里插入图片描述
在这里插入图片描述

挪动数据和插入

在这里插入图片描述
在这里插入图片描述

6.4 erase 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
iterator erase(iterator pos)

{

    assert(pos >= begin());

    assert(pos < end());

    assert(!empty());



    iterator i = pos + 1;

    while (i != end())

    {

        \*(i - 1) = \*i;

        ++i;

    }

    --\_finish;

    return pos;



}

**思路**:erase 函数用于删除指定位置的元素。首先进行位置合法性检查,然后将删除位置之后的元素依次向前移动一位,最后将 \_finish 指针向前移动一位。函数返回指向删除位置的迭代器。

==可借助下面两张图来理解erase的过程==

`1.挪动数据

2.修改finish`

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

八、其他函数

7.1 swap 函数

代码语言:cpp
代码运行次数:0
运行
AI代码解释
复制
void swap(vector<T> v)//两个vector对象的交换

{

    std::swap(\_start, v.\_start);

    std::swap(\_finish, v.\_finish);

    std::swap(\_endofstorage, v.\_endofstorage);

}

**思路**:swap 函数用于交换两个 vector 对象的内容。通过交换三个成员指针,实现了两个 vector 对象的快速交换。

九、实现亮点与注意事项

  1. **深拷贝实现**:通过逐个元素赋值确保自定义类型正确拷贝,再次重申一句在C++中所有容器相关的拷贝中,都要用深拷贝来代替浅拷贝
  2. **异常安全**:采用“拷贝 - 交换”模式保证赋值操作的原子性。
  3. **迭代器安全**:在扩容时通过记录相对位置保持迭代器有效性(具体实现细节将在后续文章深入解析)。

结尾总结

本文通过自主实现的 xc::vector 类,展示了动态数组容器的核心实现技术。

需要特别注意的是,虽然当前实现通过记录相对位置等手段初步解决了迭代器失效问题,但不同操作(如 reserveinserterase)引发的迭代器失效机制与应对策略仍需深入探讨。建议读者持续关注后续文章《,我们将结合具体代码与测试用例,系统分析迭代器失效的根本原因及解决方案。

提示:在实际开发中,建议优先使用标准库 std::vector。若需自定义实现,务必严格遵循 C++ 对象生命周期管理规则,并通过单元测试验证关键功能。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C++之模拟实现vector
因为学习了vector的相关知识,了解了vector大部分接口的底层实现原理,所以我决定自己模拟实现一个mini版的vector类,用来加深对vector各方面知识的理解。 如果有错误或不足之处,还望各位读者小伙伴们指出。
摘星
2023/04/28
3440
C++之模拟实现vector
C++初阶:适合新手的手撕vector(模拟实现vector)
上次讲了常用的接口:C++初阶:容器(Containers)vector常用接口详解 今天就来进行模拟实现啦
是Nero哦
2024/02/12
5700
C++初阶:适合新手的手撕vector(模拟实现vector)
C++初阶-vector的使用及模拟
C++vector的使用及模拟 零、前言 一、什么是vector 二、vector的常用接口说明 1、vector对象常用构造 2、vector对象容量操作 3、vector对象访问及遍历操作 4、vector对象修改操作 5、vector迭代器失效问题 三、vector剖析及模拟实现 1、vector框架及常用接口展示 2、vector模拟常用接口具体细节 3、使用memcpy拷贝问题 4、动态二维数组理解 零、前言 本章将学习C++中的vector类,掌握其使用以及模拟实现 一、什么是vector
用户9645905
2022/11/30
5440
C++初阶-vector的使用及模拟
【c++】vector以及vector的模拟实现
https://cplusplus.com/reference/vector/vector/
用户10925563
2024/06/04
1420
【c++】vector以及vector的模拟实现
【c++】vector模拟实现与深度剖析
我们首先定义了一个模版类,这里的vector三个成员均为迭代器,而Vector的迭代器是一个原生指针,我们这里为其定义别名iterator
用户11029103
2024/04/25
1210
【c++】vector模拟实现与深度剖析
【C++】模拟实现vector
https://blog.csdn.net/weixin_72357342/article/details/139740266?spm=1001.2014.3001.5501而在本次项目中我们的目标是模拟实现一个vector对象集合类模板: 该对象集合包含三个成员变量,分别是:
修修修也
2024/08/06
970
【C++】模拟实现vector
【C++】vector的底层剖析以及模拟实现
vector是表示可变大小数组的序列容器。 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。
用户10923276
2024/03/28
2190
【C++修炼之路】10. vector类
使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习
每天都要进步呀
2023/03/28
4890
【C++修炼之路】10. vector类
【C++】简化源码——vector的模拟实现
这本质上与T*a,size_t size,size_t capacity是类似的:
平凡的人1
2023/10/15
2100
【C++】简化源码——vector的模拟实现
【C++篇】深入剖析C++ Vector底层源码及实现机制
接上篇:【C++篇】探索STL之美:vector容器讲解_c++vector容器-CSDN博客
熬夜学编程的小王
2024/11/21
3140
【C++篇】揭秘STL vector:高效动态数组的深度解析(从使用到模拟实现)
本文核心内容为vector的介绍及使用和对vector的模拟实现。本文包含很多易错点,都是我在学习vector过程中踩过的坑。因为自己淋过雨,希望可以为你们撑一把伞!共勉。
我想吃余
2025/05/20
1820
【C++篇】揭秘STL vector:高效动态数组的深度解析(从使用到模拟实现)
C++从入门到精通(第七篇) :vector深度剖析及模拟实现
vector(size_type n, const value_type& val = value_type())
雪芙花
2022/11/07
5710
C++从入门到精通(第七篇) :vector深度剖析及模拟实现
vector介绍与使用【C++】
C++中的vector是一个动态数组,它可以根据需要自动调整大小。它存储在连续的内存块中,提供了快速的随机访问和插入操作,但删除操作可能导致内存的移动。vector是STL(标准模板库)的一部分,可以容纳任何类型的元素,包括内置类型和用户定义的类型。使用vector时,需要包含头文件,并通过std命名空间访问。vector还提供了许多成员函数,如push_back()、pop_back()、size()等,以支持各种操作。
鲜于言悠
2024/05/10
2150
vector介绍与使用【C++】
【C++】STL---vector
vector 学习时一定要学会查看文档:vector文档介绍,vector 在实际中非常的重要,在实际中我们熟悉常见的接口就可以,下面我们直接开始模拟实现,在模拟实现中我们实现的是常见的接口,并且会在实现中讲解它们的使用以及注意事项。
YoungMLet
2024/03/01
1030
【C++】STL---vector
手撕vector
有了前面模拟实现string的经验,vector对大家来说也不会算很难。vector本质也就是一个空间可以动态变化的数组,所以这里就挑一些些容易踩坑的地方讲解一下,在最后会附上我的完整代码。
始终学不会
2023/04/06
4400
手撕vector
【STL】之 vector 使用方法及模拟实现
成员变量的定义: 对于vector容器,我们首先采用三个成员变量去进行定义,分别是:
用户11316056
2024/10/16
1110
【STL】之 vector 使用方法及模拟实现
【C++/STL】vector(常见接口、模拟实现、迭代器失效)
注意上面if语句的判断条件,找不到时,返回值是自己给的last,即上面的v.end()。
秦jh
2024/05/28
1830
【C++/STL】vector(常见接口、模拟实现、迭代器失效)
【C++】vector(下)--上篇
首先我们需要在头文件stl_vector.h中了解vector的构成,它的三个私有成员分别是迭代器start、迭代器finish、迭代器endofstorage,分别指向vector的头、size的尾、capacity的尾
s-little-monster
2024/09/09
1280
【C++】vector(下)--上篇
C++:手把手教你手撕vector
vector.hpp是源文件里面是vector的实现,main函数是测试用的,因为一个项目只能有一个main入口对吧;
啊QQQQQ
2025/02/20
1140
C++:手把手教你手撕vector
【c++丨STL】vector模拟实现
本篇文章,我们将深入探讨vector的底层实现原理,并尝试模拟实现其结构以及一些常用接口。
ephemerals__
2024/11/13
1150
【c++丨STL】vector模拟实现
相关推荐
C++之模拟实现vector
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验