C++的动态数组以及std:vector的优化

news2024/11/23 13:34:37

文章目录

    • 静态数组
    • 动态数组
      • 代码背景
      • 第一种打印方式:使用 `for` 循环和索引
        • 解释
      • 第二种打印方式:使用基于范围的 `for` 循环
        • 解释
        • 改进方式:避免拷贝
      • 总结
      • 清理数组
    • 代码示例
      • 代码分析
      • 输出结果
      • 总结
    • 代码示例
      • 代码详解
      • 总结
        • 使用 `reserve` 的优点:
        • 使用 `emplace_back` 的优点:

在C++编程中,数组是一种常见的数据结构,用于存储固定大小的相同类型的数据。然而,在实际应用中,数据量往往难以预估,这时静态数组的固定大小限制就显得尤为不便。为了解决这一问题,C++引入了动态数组的概念。动态数组允许程序在运行时根据实际需求灵活地分配内存空间,从而有效地处理数据大小不确定的情况。

在本文中,我们将深入探讨C++中动态数组的实现方式,包括手动内存管理、常见的动态数组操作,以及C++标准库中提供的std::vector容器。通过这些内容,您将掌握如何在C++中有效地使用动态数组来编写更灵活和高效的代码。

静态数组

在这里插入图片描述

动态数组

在这段代码中,你展示了两种不同的方式来遍历并打印 std::vector<Vertex> 中的元素。这两种方式各有特点,适合不同的使用场景。让我们详细分析这两种方式。

代码背景

假设 Vertex 是一个自定义的结构体或类,包含了三个整数作为其成员变量,例如:

struct Vertex {
    int x, y, z;

    // 重载 << 操作符,使得 Vertex 对象可以直接用于 std::cout
    friend std::ostream& operator<<(std::ostream& os, const Vertex& v) {
        os << "Vertex(" << v.x << ", " << v.y << ", " << v.z << ")";
        return os;
    }
};

第一种打印方式:使用 for 循环和索引

for (int i = 0; i < vertices.size(); i++)
    std::cout << vertices[i] << std::endl;
解释
  1. 访问方式: 这段代码使用经典的 for 循环,通过索引 i 访问 vertices 中的每一个元素。
  2. 打印输出: 通过 vertices[i] 获取到每个 Vertex 对象,并利用重载的 << 操作符将对象输出到控制台。
  3. 优点:
    • 灵活性高:可以通过索引轻松访问和修改特定元素。
    • 兼容性:这种方式适用于任何标准容器,如数组、std::vectorstd::deque等。
  4. 缺点:
    • 代码冗长:需要手动管理索引。
    • 易错性:如果不小心,可能会访问越界的索引。

第二种打印方式:使用基于范围的 for 循环

for (Vertex v : vertices)
    std::cout << v << std::endl;
解释
  1. 访问方式: 这段代码使用了 C++11 引入的基于范围的 for 循环,它可以自动遍历 vertices 中的每一个元素。
  2. 打印输出: Vertex v 是从 vertices 容器中拷贝出来的元素,使用 v 直接进行打印。
  3. 优点:
    • 简洁易读:代码更加简洁,不需要管理索引,降低了出错的可能性。
    • 安全性:不需要担心数组越界问题。
  4. 缺点:
    • 拷贝开销:由于 v 是一个拷贝对象,如果 Vertex 对象较大,这可能会带来一些性能开销。不过,如果 Vertex 是一个轻量级的结构体,这种开销可以忽略不计。
    • 只读访问:默认情况下,基于范围的 for 循环会创建一个拷贝,这意味着你不能直接修改原容器中的元素。
改进方式:避免拷贝

如果你希望避免不必要的拷贝,可以使用引用:

for (const Vertex& v : vertices)
    std::cout << v << std::endl;
  • 引用访问: 使用 const Vertex& v 表示我们通过引用而不是拷贝来访问每个元素。这不仅避免了拷贝的开销,还保证了容器元素不会被修改。

总结

  • 使用索引的 for 循环: 适用于需要灵活操作元素或需要进行修改的场景。这种方式更传统,也更加通用,但代码稍显繁琐。
  • 基于范围的 for 循环: 更加简洁易读,适用于需要遍历整个容器且不需要修改元素的场景。如果需要避免拷贝,可以使用引用。

两种方法各有优劣,选择哪一种主要取决于代码的上下文和具体需求。

清理数组

在这里插入图片描述

代码示例

#include <iostream>
#include <vector>

struct Vertex {
    int x, y, z;

    // 重载 << 操作符,使得 Vertex 对象可以直接用于 std::cout
    friend std::ostream& operator<<(std::ostream& os, const Vertex& v) {
        os << "Vertex(" << v.x << ", " << v.y << ", " << v.z << ")";
        return os;
    }
};

// 函数声明
void Function(const std::vector<Vertex>& vertices) {
    // 函数内部没有内容,仅作传递演示
}

int main() {
    std::vector<Vertex> vertices;

    // 向vector中添加元素
    vertices.push_back({1, 2, 3});
    vertices.push_back({4, 5, 6});

    // 调用Function并传递vertices
    Function(vertices);

    // 遍历并打印vector内容
    for (int i = 0; i < vertices.size(); i++)
        std::cout << vertices[i] << std::endl;

    // 删除vector中的第二个元素
    vertices.erase(vertices.begin() + 1);

    // 再次遍历并打印vector内容
    for (int i = 0; i < vertices.size(); i++)
        std::cout << vertices[i] << std::endl;

    return 0;
}

代码分析

  1. 定义 Vertex 结构体:

    • Vertex 结构体包含三个整数 x, y, z,表示一个三维点。
    • 重载了 << 操作符,方便直接将 Vertex 对象输出到标准输出流。
  2. 函数 Function:

    • Function 函数接受一个 std::vector<Vertex> 的常量引用作为参数。这意味着传递给该函数的 vertices 向量将不会被修改。
    • 该函数内部没有实际操作,但展示了如何传递一个 std::vector 的引用,避免不必要的拷贝。
  3. main 函数中:

    • 创建 vertices 向量: 使用 std::vector<Vertex> 创建了一个名为 vertices 的向量,并通过 push_back 方法添加了两个 Vertex 对象。
    • 调用 Function 函数: 将 vertices 向量传递给 Function 函数。在函数内部,尽管没有进行操作,但由于参数是通过常量引用传递的,这种方式避免了整个向量的拷贝。
    • 遍历并打印 vertices 向量: 使用 for 循环遍历向量中的每个 Vertex 对象,并使用 std::cout 将其输出到控制台。此时会打印出 vertices 向量中的两个元素。
    • 删除第二个元素: 使用 erase 方法删除了向量中的第二个元素(索引为1)。erase 会从向量中移除指定位置的元素,且后续元素会前移填补空缺,向量的大小会减1。
    • 再次遍历并打印 vertices 向量: 删除元素后,再次遍历并打印 vertices,此时只会输出剩下的一个元素。

输出结果

假设 Vertex 重载的 << 操作符输出格式为 Vertex(x, y, z),那么程序的输出将如下:

Vertex(1, 2, 3)
Vertex(4, 5, 6)
Vertex(1, 2, 3)

总结

这段代码展示了如何在C++中使用 std::vector,包括如何添加元素、传递给函数、遍历元素、删除元素等常见操作。通过使用常量引用作为函数参数,可以避免不必要的拷贝操作,从而提高程序效率。与此同时,erase 操作能够灵活地修改向量内容,是管理动态数据的常用手段。

这段代码使用了C++标准库中的std::vectoremplace_back函数来创建和管理一个Vertex对象的动态数组。让我们详细分析每一步的操作及其目的。

代码示例

std::vector<Vertex> vertices;
vertices.reserve(3);
vertices.emplace_back(1, 2, 3);
vertices.emplace_back(4, 5, 6);
vertices.emplace_back(7, 8, 9);

代码详解

  1. 创建 std::vector<Vertex> 向量:

    std::vector<Vertex> vertices;
    
    • 创建了一个空的std::vector对象,名为vertices,用于存储Vertex类型的对象。
    • std::vector 是一个动态数组,可以自动调整其大小以容纳元素。
  2. 调用 reserve(3):

    vertices.reserve(3);
    
    • reserve 函数请求将 vertices 向量的容量增加到至少3个元素的大小。这样做可以避免在向向量中添加元素时频繁的内存分配操作,提高性能。
    • 需要注意的是,reserve 只是预分配内存,但不会改变 vertices 的实际大小或内容。此时 vertices 的大小仍为0,但它的容量为3。
  3. 使用 emplace_back 添加元素:

    vertices.emplace_back(1, 2, 3);
    vertices.emplace_back(4, 5, 6);
    vertices.emplace_back(7, 8, 9);
    
    • emplace_backstd::vector 的一个成员函数,用于在向量末尾直接构造一个元素,而无需先创建临时对象再拷贝或移动到向量中。
    • 在这里,emplace_back(1, 2, 3) 等价于 vertices.push_back(Vertex(1, 2, 3)),但 emplace_back 更高效,因为它直接在向量的内存中构造对象,避免了额外的临时对象创建和移动操作。
    • 每次调用 emplace_back 都会向 vertices 向量中添加一个 Vertex 对象。经过三次调用后,vertices 向量中包含三个 Vertex 对象,分别存储 (1, 2, 3)(4, 5, 6)(7, 8, 9)

总结

这段代码的目的是创建一个可以容纳 Vertex 对象的 std::vector,预先为它分配足够的内存(容量为3),然后使用 emplace_back 函数将三个 Vertex 对象直接添加到向量中。这样做的好处是提高了向量操作的效率,尤其是在频繁添加对象的情况下。

使用 reserve 的优点:
  • 性能优化: 通过预先分配内存,可以减少向量动态扩展时的内存重新分配操作,从而提高程序的效率。
使用 emplace_back 的优点:
  • 高效对象构造: emplace_back 直接在向量内部构造对象,避免了临时对象的创建和销毁,适用于对象构造过程较复杂或较重的情况。

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

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

相关文章

【考研数学】定积分应用——旋转体体积的计算(一文以蔽之)

目录 一、如何计算旋转体体积&#xff1f;思考一个小例子 二、旋转体体积的二重积分表达式 三、用真题&#xff0c;小试牛刀 定积分的应用中&#xff0c;有一类题是求解旋转体的体积问题。 相较于记忆体积计算公式&#xff0c;有一种通法求解体积更不容易出错&#xff1a;二重…

系统数据库介绍及实践

目录 案例 【题目】 【问题 1】(8 分) 【问题 2】(13 分) 【问题 3】(4 分) 【答案】 【问题 1】解析 【问题 2】解析 【问题 3】解析 相关推荐 案例 阅读以下关于应用系统数据架构的说明&#xff0c;在答题纸上回答问题 1 至问题 3。 【题目】 某软件公司拟开发一套…

svn文件定时全量备份

在win11操作系统中&#xff0c;使用定时任务脚本的方式实现对SVN文件的定时备份 SVN备份脚本 1 创建脚本simpleBackup.bat 该脚本主要用于实现备份过程的信息展示 echo 正在备份版本库%1...... md %BACKUP_DIRECTORY%\%2 %SVN_HOME%\bin\svnadmin hotcopy %1 %BACKUP_D…

蓝桥杯 双周赛 第16场 强者赛 题目复盘 (2024年8月10日)

6. 花魁之争 解题思路&#xff1a; 根据题意&#xff0c;对于每一次操作&#xff0c;每个仙女来说都取最优解&#xff0c;那第一次每个仙女都操作一次&#xff0c;这时候胜出的仙女&#xff0c;是一定赢的。所以&#xff0c;只要计算n个字符串操作一次的最优字符串&#xff0c;…

HarmonyOS NEXT - 通过 module 模块化引用公共组件和utils

demo 地址: https://github.com/iotjin/JhHarmonyDemo 代码不定时更新&#xff0c;请前往github查看最新代码 HarmonyOS NEXT 一、HAP & HSP & HAR介绍HAP官方介绍HAR官方介绍HSP官方介绍怎么理解App、HAP、HAR的关系HAR如何转换为HSPHSP模块如何快速切换成HAR模块 二…

【JavaSE】基础知识复习(六)(不全)

1.File与IO流 File类就是代表系统的文件 / 目录&#xff0c;IO流是用来处理File类的 File类 构造器 分隔符&#xff08;三种&#xff09; 第三种File.separator是跨平台的&#xff0c;获取当前操作系统的分隔符 常用方法 length() 返回文件大小(字节)&#xff0c;如果是目录&am…

EcoDev Studio 与 gitlab【拉取项目,切换分支,再修改提交】

1 安装git工具 https://blog.csdn.net/mukes/article/details/115693833 2 创建空项目 3 推送gitlab 1、进入本地该项目目录下&#xff0c;右键Git Bash Here打开git命令窗口 2、初始化本地仓库&#xff1a; git init将本地项目的所有文件添加到暂存区&#xff1a; git a…

EMC学习笔记2——电磁兼容问题分析

分析一个电磁兼容问题一般从三方面入手&#xff0c;分别是骚扰源、敏感源、耦合路径。解决掉其中一个问题&#xff0c;就能解决大部分的电磁兼容问题。 例如&#xff1a;当骚扰源是雷电时&#xff0c;敏感源是电子线路时&#xff0c;我们需要消除的就是耦合电路。 耦合路径就是…

吴恩达机器学习-C2W1L1-神经元和层

在本实验中&#xff0c;我们将探索神经元/单元和层的内部工作原理。特别地&#xff0c;这个实验将与你在课程1中掌握的模型&#xff0c;回归/线性模型和逻辑模型进行类比。本实验室将介绍Tensorflow&#xff0c;并演示如何在该框架中实现这些模型。 包 Tensorflow和Keras Te…

Ubuntu中编译使用ANTs(医学图像配准)含github无法访问问题解决

目录 第一步、修改hosts文件 1.打开https://github.com.ipaddress.com/ 2.打开https://fastly.net.ipaddress.com/github.global.ssl.fastly.net#ipinfo 3.打开hosts文件&#xff0c;并在文件末尾添加如下内容 第二步、编译ANTs 1&#xff09;首先安装git、cmake以及c编译…

基于DVWA-Brute Force(LowMedium)的渗透测试

Brute force主要是通过爆破达到渗透目的&#xff1a; Low 查看源代码&#xff1a; <?phpif( isset( $_GET[ Login ] ) ) {// Get username$user $_GET[ username ];// Get password$pass $_GET[ password ];$pass md5( $pass );// Check the database$query "SE…

流量分析0.o

[陇剑杯 2021]签到 此时正在进行的可能是__________协议的网络攻击。&#xff08;如有字母请全部使用小写&#xff0c;填写样例&#xff1a;http、dns、ftp&#xff09;。得到的flag请使用NSSCTF{}格式提交。 过滤协议 例子:tcp&#xff0c;udp&#xff0c;arp&#xff0c;i…

基于SpringBoot和Leaflet的全国省会城市风景区分布分析实践

目录 前言 一、空间数据表的设计与实现 1、城市信息数据表 2、风景区信息表设计与实现 3、数据库查询应用 二、WebGIS程序设计与实现 1、使用select中转义字符的使用 2、Leaflet空间信息展示 三、省会城市风景区可视化成果展示 1、哈尔滨市风景区分析 2、南京市风景区…

【6大设计原则】依赖倒置原则:构建灵活软件架构的基石 - 通过代码实例深入解析

1.引言 1.1为什么要学习依赖倒置原则 在软件开发过程中&#xff0c;我们经常需要对代码进行修改和扩展。如果代码之间的耦合度过高&#xff0c;那么在进行修改或扩展时&#xff0c;可能会对其他部分的代码产生影响&#xff0c;甚至引发错误。这就要求我们在编写代码时&#xf…

TransFormer学习的一些重要链接

以下是自己在学习和掌握TransFormer中的一些重要链接: 1. 这篇讲解的我看的最明白和清楚&#xff0c;但是仍要结合代码来加深理解。Transformer模型详解&#xff08;图解最完整版&#xff09;https://zhuanlan.zhihu.com/p/338817680 2. 七月在线-TransFormer的文本分类http…

LabVIEW多协议智能流水线控制与监控系统

在自动化流水线系统&#xff0c;实现对流水线传送带、机械臂、报警系统、扫码机、喷码机等设备的高效控制和实时监控。该系统需要支持多种通信协议&#xff0c;包括UDP、串口、ModbusTCP、HTTP、以及MQTT协议&#xff0c;以确保各个设备间的无缝连接和数据交换。 系统架构与模…

1-安装Elasticsearch

支持一览表 | Elastic 未完待续

postgres.new:在浏览器中构建与AI协作的开源Postgres沙盒

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

海量数据处理商用短链接生成器平台 - 16

第五十四章 DWM层数据处理-Flink异步IO提升性能 第1集 IP解析地理位置问题点和Flink异步IO介绍 简介&#xff1a; IP解析地理位置问题点和Flink异步IO介绍 Flink实时计算处理存在的问题 IP解析地理位置信息&#xff0c;查询是同步查询&#xff0c;存在阻塞&#xff0c;性能不…

Unity(2022.3.38LTS) - 旋转和方向

目录 一. 旋转 二. 方向 三. 总结 四. 扩展-万向锁 一. 旋转 在 Unity 中&#xff0c;物体的旋转可以通过欧拉角和四元数来表示和操作。 欧拉角&#xff1a; 由三个角度值&#xff08;通常表示为 x、y、z &#xff09;来定义旋转。直观易懂&#xff0c;但可能会出现万向…