文章目录
- 静态数组
 - 动态数组
 - 代码背景
 - 第一种打印方式:使用 `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;
 
解释
- 访问方式: 这段代码使用经典的 
for循环,通过索引i访问vertices中的每一个元素。 - 打印输出: 通过 
vertices[i]获取到每个Vertex对象,并利用重载的<<操作符将对象输出到控制台。 - 优点: 
  
- 灵活性高:可以通过索引轻松访问和修改特定元素。
 - 兼容性:这种方式适用于任何标准容器,如数组、
std::vector、std::deque等。 
 - 缺点: 
  
- 代码冗长:需要手动管理索引。
 - 易错性:如果不小心,可能会访问越界的索引。
 
 
第二种打印方式:使用基于范围的 for 循环
 
for (Vertex v : vertices)
    std::cout << v << std::endl;
 
解释
- 访问方式: 这段代码使用了 C++11 引入的基于范围的 
for循环,它可以自动遍历vertices中的每一个元素。 - 打印输出: 
Vertex v是从vertices容器中拷贝出来的元素,使用v直接进行打印。 - 优点: 
  
- 简洁易读:代码更加简洁,不需要管理索引,降低了出错的可能性。
 - 安全性:不需要担心数组越界问题。
 
 - 缺点: 
  
- 拷贝开销:由于 
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;
}
 
代码分析
-  
定义
Vertex结构体:Vertex结构体包含三个整数x,y,z,表示一个三维点。- 重载了 
<<操作符,方便直接将Vertex对象输出到标准输出流。 
 -  
函数
Function:Function函数接受一个std::vector<Vertex>的常量引用作为参数。这意味着传递给该函数的vertices向量将不会被修改。- 该函数内部没有实际操作,但展示了如何传递一个 
std::vector的引用,避免不必要的拷贝。 
 -  
在
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::vector和emplace_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);
 
代码详解
-  
创建
std::vector<Vertex>向量:std::vector<Vertex> vertices;- 创建了一个空的
std::vector对象,名为vertices,用于存储Vertex类型的对象。 std::vector是一个动态数组,可以自动调整其大小以容纳元素。
 - 创建了一个空的
 -  
调用
reserve(3):vertices.reserve(3);reserve函数请求将vertices向量的容量增加到至少3个元素的大小。这样做可以避免在向向量中添加元素时频繁的内存分配操作,提高性能。- 需要注意的是,
reserve只是预分配内存,但不会改变vertices的实际大小或内容。此时vertices的大小仍为0,但它的容量为3。 
 -  
使用
emplace_back添加元素:vertices.emplace_back(1, 2, 3); vertices.emplace_back(4, 5, 6); vertices.emplace_back(7, 8, 9);emplace_back是std::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直接在向量内部构造对象,避免了临时对象的创建和销毁,适用于对象构造过程较复杂或较重的情况。 

















