C++ Vector 容器的模拟实现及应用详解

news2024/10/17 19:21:45

目录

一、什么是 vector

二、vector 的核心特性

1. 构造函数

2. 容量管理

3. 增删查改操作

三、vector 的扩容机制

四、迭代器失效问题

五、vector 的模拟实现

1. push_back 实现

2. 扩容的细节

六、memcpy 和深浅拷贝问题

七、总结


在 C++ 标准模板库(STL)中,vector 是最常用的动态数组容器之一。它提供了动态调整大小的数组结构,同时保留了数组随机访问的高效特性。本文将深入剖析 vector 的核心实现原理,并通过模拟实现的方式,帮助大家更好地理解其工作机制。


一、什么是 vector

vector 是 C++ STL 中的一个动态数组容器,它可以自动管理内存,并根据需要动态增加或减少存储容量。与传统数组相比,vector 具备以下优势:

  1. 动态大小vector 可以根据需要动态增长或缩小,而不需要在初始化时指定固定大小。
  2. 高效的随机访问:与数组一样,vector 允许通过下标进行常量时间(O(1))的随机访问。
  3. 自动内存管理vector 会在容量不足时自动扩展空间,并且可以通过 reserve 减少频繁扩容带来的性能开销。

二、vector 的核心特性

在使用 vector 时,需要掌握其核心接口和功能,以便在实际项目中应用自如。下面简要介绍 vector常用接口和功能

1. 构造函数

vector 提供多种构造方式,包括无参构造、带初始值的构造、使用迭代器的构造等。以下是常见构造函数的形式:

std::vector<int> v1;                          // 默认构造,创建一个空的vector
std::vector<int> v2(10, 5);                   // 创建包含10个元素,每个元素初始化为5
std::vector<int> v3(v2.begin(), v2.end());    // 使用迭代器初始化
std::vector<int> v4(v3);                      // 拷贝构造
2. 容量管理

vector 的容量管理接口包括 sizecapacityresizereserve 等,分别用于获取当前大小、当前容量、改变大小和预留空间。例如:

std::vector<int> v(10, 5);
std::cout << "Size: " << v.size() << std::endl;
std::cout << "Capacity: " << v.capacity() << std::endl;

v.reserve(20);    // 预留至少20个元素的存储空间
v.resize(15);     // 改变大小为15
3. 增删查改操作

vector 提供了非常灵活的增删查改操作,常用的有:

  • push_back():在末尾插入元素
  • pop_back():删除末尾元素
  • insert():在指定位置插入元素
  • erase():删除指定位置的元素
  • operator[]:通过下标访问元素
std::vector<int> v;
v.push_back(10);         // 尾部插入元素
v.push_back(20);
v.insert(v.begin(), 5);  // 在开头插入5
v.erase(v.begin() + 1);  // 删除第二个元素

三、vector 的扩容机制

在使用 vector 时,经常会遇到容量不足导致扩容的问题。vector 的扩容不是线性增长的,而是根据一定的倍数增长。具体的扩容机制因编译器和标准库的实现不同而有所差异。例如,在 Visual Studio 下,vector 通常以 1.5 倍的速度增长,而在 g++ 编译器下,则通常以 2 倍速度增长。

下面是一个测试 vector 扩容行为的示例:

void TestVectorExpand() {
    std::vector<int> v;
    size_t capacity = v.capacity();
    std::cout << "Initial capacity: " << capacity << std::endl;

    for (int i = 0; i < 100; ++i) {
        v.push_back(i);
        if (capacity != v.capacity()) {
            capacity = v.capacity();
            std::cout << "Capacity changed: " << capacity << std::endl;
        }
    }
}

在不同环境下运行这段代码,可以观察到 vector 扩容时容量的变化。例如,在某些环境下,输出可能是 1, 2, 4, 8, 16...,显示出每次扩容时容量翻倍的行为。


四、迭代器失效问题

vector 的迭代器在某些操作下可能会失效,尤其是在插入或删除操作涉及到扩容时。常见的迭代器失效情况包括:

  1. 扩容:当 vector 扩容时,旧的内存空间会被释放,指向该空间的迭代器将变为无效。
  2. 插入和删除:当元素插入或删除时,后续元素的位置可能发生移动,从而导致迭代器失效。

例如,以下代码会导致迭代器失效,从而产生未定义行为:

std::vector<int> v{1, 2, 3, 4, 5};
auto it = v.begin();
v.push_back(6);    // 可能会导致扩容,it 失效
std::cout << *it;  // 未定义行为

解决迭代器失效的常见方法是在扩容或插入、删除操作后重新获取迭代器:

v.push_back(6);
it = v.begin();    // 重新获取有效的迭代器
std::cout << *it;

五、vector 的模拟实现

为了更好地理解 vector 的工作原理,我们可以通过模拟实现一个简化版的 vector。以下是一个基本的 vector 模拟实现示例:

#include <iostream>
#include <memory>

template <typename T>
class MyVector {
public:
    MyVector() : size_(0), capacity_(0), data_(nullptr) {}

    // 析构函数,释放内存
    ~MyVector() {
        if (data_) {
            delete[] data_;
        }
    }

    // 插入元素
    void push_back(const T& value) {
        if (size_ == capacity_) {
            resize();
        }
        data_[size_++] = value;
    }

    // 获取元素
    T& operator[](size_t index) {
        return data_[index];
    }

    // 获取大小
    size_t size() const {
        return size_;
    }

private:
    size_t size_;
    size_t capacity_;
    T* data_;

    // 扩容操作
    void resize() {
        size_t new_capacity = (capacity_ == 0) ? 1 : capacity_ * 2;
        T* new_data = new T[new_capacity];

        // 复制旧数据到新空间
        for (size_t i = 0; i < size_; ++i) {
            new_data[i] = data_[i];
        }

        // 释放旧空间
        if (data_) {
            delete[] data_;
        }

        data_ = new_data;
        capacity_ = new_capacity;
    }
};

int main() {
    MyVector<int> v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }

    for (int i = 0; i < v.size(); ++i) {
        std::cout << v[i] << " ";
    }

    return 0;
}

这个 MyVector实现了一个简单的动态数组容器,支持 push_back 操作和下标访问。每当容量不足时,resize 函数会将容量扩大一倍,并将旧数据复制到新空间。

1. push_back 实现

push_back 的实现检查当前大小是否等于容量,如果容量不足,则调用 resize 进行扩容。扩容后的新元素会添加到数组末尾。

2. 扩容的细节

在扩容时,我们首先创建一个新数组,其容量是原容量的两倍,然后将旧数组的数据逐个复制到新数组中,最后释放旧数组的内存。这一操作确保了 vector 能够动态调整存储空间的大小,同时保证已有的数据不丢失。


六、memcpy 和深浅拷贝问题

在实现 vector 时,某些情况下可能会使用 memcpy 来加速内存拷贝操作。然而,memcpy 只进行浅拷贝,对于简单类型(如 int)没有问题,但对于复杂对象(如包含指针的类对象)则可能引发问题。

例如,下面的代码展示了错误的 memcpy 使用场景:

#include <iostream>
#include <cstring>

class MyString {
public:
    MyString(const char* str) {
        size_t len = strlen(str) + 1;
        data_ = new char[len];
        memcpy(data_, str, len);
    }

    ~MyString() {
        delete[] data_;
    }

private:
    char* data_;
};

int main() {
    MyString s1("Hello");
    MyString s2(s1);  // 使用默认的浅拷贝

    return 0;
}

在上述代码中,s2s1 共享同一块内存空间,导致析构时重复释放内存,进而引发崩溃。因此,在涉及资源管理的情况下,应避免使用 memcpy,而应该编写正确的拷贝构造函数和赋值操作符。

七、总结

vector 作为 C++ 中最常用的容器之一,具备高效的内存管理、动态扩展、随机访问等诸多特性。通过模拟实现一个简化的 vector,我们可以更好地理解其内部工作机制,包括容量管理、扩容、迭代器失效等问题。在实际应用中,vector 的这些特性和接口让它成为一个强大的工具,可以轻松地处理动态数组相关的任务。


希望本文的深入剖析和模拟实现能帮助各位对 vector 有更加清晰的认识和理解,从而在日常开发中灵活运用该容器,提高代码的健壮性与可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2217254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考(网工)——局域网和城域网

&#x1f550;局域网基础 1️⃣局域网和城域网体系架构 IEEE&#xff08;负责链路层&#xff09; 2️⃣局域网拓扑结构 局域网的主要特征由网络的拓扑结构、所采用的协议类型&#xff0c;以及介质访问控制方法决定。局域网的拓扑结构是指连接网络设备的传输介质的铺设形式&am…

爬虫逆向学习(十二):一个案例入门补环境

此分享只用于学习用途&#xff0c;不作商业用途&#xff0c;若有冒犯&#xff0c;请联系处理 反爬前置信息 站点&#xff1a;aHR0cDovLzEyMC4yMTEuMTExLjIwNjo4MDkwL3hqendkdC94anp3ZHQvcGFnZXMvaW5mby9wb2xpY3k 接口&#xff1a;/xjzwdt/rest/xmzInfoDeliveryRest/getInfoDe…

AI驱动的零售未来:打造无缝、智能、个性化的购物新世界

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 Shelly AI 工具集&#xff1a; 100个AI&am…

【私有云盘搭建】Portainer CE部署NextCloud,轻松实现公网访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【Linux系统查看磁盘占用情况】

文章目录 概要df 命令du 命令ls 命令 概要 在 Linux 系统中&#xff0c;查看磁盘占用情况可以使用以下几种常用的命令&#xff1a;df查看系统磁盘空间&#xff1b;du&#xff1a;查看目录的磁盘使用情况&#xff1b;ls&#xff1a;列出目录内容的基本命令 df 命令 可以显示文…

JAVA就业笔记7——第二阶段(4)

课程须知 A类知识&#xff1a;工作和面试常用&#xff0c;代码必须要手敲&#xff0c;需要掌握。 B类知识&#xff1a;面试会问道&#xff0c;工作不常用&#xff0c;代码不需要手敲&#xff0c;理解能正确表达即可。 C类知识&#xff1a;工作和面试不常用&#xff0c;代码不…

Gin框架操作指南08:日志与安全

官方文档地址&#xff08;中文&#xff09;&#xff1a;https://gin-gonic.com/zh-cn/docs/ 注&#xff1a;本教程采用工作区机制&#xff0c;所以一个项目下载了Gin框架&#xff0c;其余项目就无需重复下载&#xff0c;想了解的读者可阅读第一节&#xff1a;Gin操作指南&#…

【C++】红黑树模拟实现map和set

本篇基于上篇红黑树的代码来实现&#xff1a; 【C】红黑树-CSDN博客 关于map和set可以看&#xff1a;​​​​ 【C】map和set的介绍和使用-CSDN博客 改造红黑树 map底层是红黑树的KV模型&#xff0c;set是红黑树的K模型&#xff0c;按理来说&#xff0c;应该设计两种红黑树来…

企业培训平台开发指南:基于在线教育系统源码的实现路径解析

本篇文章&#xff0c;小编将通过对在线教育系统源码的解读&#xff0c;深入探讨企业培训平台的开发路径&#xff0c;帮助企业高效构建适合自身需求的培训系统。 一、企业培训平台的需求分析 在开发企业培训平台之前&#xff0c;首先要对企业的实际需求进行充分分析。每个企业…

各种开发编程软件的下载方法--visio,navicat,pycharm,matlab等

各类开发编程类软件的下载方法 一、需要付费的 之前在网络上有很多显示可以免费下载的软件&#xff0c;不是各种在解压时需要密码的&#xff0c;就是有各种病毒的&#xff0c;绕一圈可能还得收费。 最早之前用的是 “A软件安装管家” 这个公众号里的&#xff0c;后来停更了&…

【优选算法篇】双指针的华丽探戈:深入C++算法殿堂的优雅追寻

文章目录 C 双指针详解&#xff1a;进阶题解与思维分析前言第一章&#xff1a;有效三角形的个数1.1 有效三角形的个数示例 1&#xff1a;示例 2&#xff1a;解法一&#xff08;暴力求解&#xff09;解法二&#xff08;排序 双指针&#xff09;易错点提示代码解读 第二章&#…

C++的魔法世界:类和对象的终章

文章目录 一、再探构造函数二、类型转换2.1隐式类型转换2.2内置类型的类型转化2.3explicit关键字2.4多参数构造 三、static成员四、友元五、内部类内部类的特性 六、匿名对象 一、再探构造函数 类和对象(中)里介绍的构造函数&#xff0c;使用的是赋值实现成员变量的初始化。而…

【word】文章里的表格边框是双杠

日常小伙伴们遇到word里插入的表格&#xff0c;边框是双杠的&#xff0c;直接在边框和底纹里修改边框的样式就可以&#xff0c;但我今天遇到的这个有点特殊&#xff0c;先看看表格在word里的样式是怎么样&#xff0c;然后我们聊聊如何解决。 这个双杠不是边框和底纹的设置原因…

亚洲 Web3 市场:Q3 监管变化与市场驱动力探析

概述&#xff1a; 亚洲的 Web3 市场在2024年第三季度继续表现出强劲增长势头。得益于技术精通的人口基础、政府的积极政策导向和企业的大规模参与&#xff0c;韩国、日本、越南等国家已然走在行业前沿。此外&#xff0c;随着越来越多的监管框架落地&#xff0c;区块链创新不断…

Ubuntu20.04下安装多CUDA版本,以及后续切换卸载

本方案的前提是假设机子上已经有一个版本的cuda&#xff0c;现在需要支持新的torch2.1.2和torchvision0.16.2&#xff0c;于是来安装新的cuda 一、选择版本 如果我想安装支持torch2.1.2的cuda版本&#xff0c;到官网&#xff08;https://pytorch.org/get-started/previous-ve…

【Python文件操作】掌握文件读写和目录管理的技巧!

【Python文件操作】掌握文件读写和目录管理的技巧&#xff01; 在现代编程中&#xff0c;文件操作是不可避免的一部分&#xff0c;尤其是在处理数据、日志、配置文件等场景下。Python 提供了强大而简洁的文件操作方法&#xff0c;可以轻松完成文件的读取、写入和目录管理等操作…

005_django基于Python的乡村居民信息管理系统设计与实现2024_106f2qg9

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

SpringMVC源码-异常处理机制

定义一个异常处理类TestErrorController: Controller public class TestErrorController {RequestMapping("/exception")public ModelAndView exception(ModelAndView view) throws ClassNotFoundException {view.setViewName("index");throw new ClassNot…

Mysql主从集群搭建+分库分表+ShardingSphere(实战)

什么是 ShardingSphere 介绍 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为分布式数据库&#xff0c;并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 Apache ShardingSphere 设计哲学为 Database Plus&#xff0c;…

CRMEB标准版Mysql修改sql_mode

数据库配置 1.宝塔控制面板-软件商店-MySql-设置 2.点击配置修改&#xff0c;查找sql-mode或sql_mode &#xff08;可使用CtrlF快捷查找&#xff09; 3.复制 NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 然后替换粘贴&#xff0c;保存 注&#xff1a;MySQL8.0版本的 第三步用…