top
本文目录
1. 什么是 Vector?
2. Vector 的基本使用
2.1 头文件
2.2 声明和初始化
2.3 访问元素
2.4 修改元素
3. Vector 的常用方法
3.1 容量相关
3.2 元素添加和删除
3.3 其他常用方法
4. 迭代器
5. Vector 的高级应用
5.1 作为函数参数和返回值
5.2 Vector 与算法
5.3 Vector 存储自定义类型
6. Vector 的性能考虑
7. 总结

C++ Vector使用指南:常用方法与技巧

C++ Vector 使用指南:常用方法与技巧

std::vector 是 C++ 标准模板库 (STL) 中最常用的序列容器之一。它提供了一种动态数组的功能,可以根据需要自动调整大小,同时保持高效的元素访问和管理。本文将深入探讨 std::vector 的各种用法、常用方法、技巧以及一些高级应用,帮助您充分掌握这个强大的工具。

1. 什么是 Vector?

std::vector 是一个封装了动态大小数组的模板类。与普通数组相比,vector 的优势在于:

  • 动态大小: vector 可以根据元素的添加和删除自动调整其大小,无需手动管理内存。
  • 内存连续: vector 中的元素在内存中是连续存储的,这使得访问元素非常高效(通过索引随机访问)并且与 C 风格数组兼容。
  • 丰富的接口: vector 提供了大量方便的方法来操作元素,如插入、删除、查找等。
  • 自动内存管理: vector 使用 RAII(Resource Acquisition Is Initialization)原则,在对象生命周期结束时自动释放其占用的内存,防止内存泄漏。

2. Vector 的基本使用

2.1 头文件

要使用 std::vector,需要包含 <vector> 头文件:

```c++

include

```

2.2 声明和初始化

以下是几种声明和初始化 vector 的方法:

```c++
// 1. 默认构造函数,创建一个空的 vector
std::vector vec1;

// 2. 创建一个包含 n 个元素的 vector,每个元素都初始化为默认值(对于 int 是 0)
std::vector vec2(5);

// 3. 创建一个包含 n 个元素的 vector,每个元素都初始化为指定的值
std::vector vec3(5, 10); // 包含 5 个值为 10 的元素

// 4. 使用初始化列表(C++11 及以后)
std::vector vec4 = {1, 2, 3, 4, 5};
std::vector vec5{1, 2, 3, 4, 5};

// 5. 使用另一个 vector 复制构造
std::vector vec6(vec4);

// 6. 使用另一个 vector 的范围构造
std::vector vec7(vec4.begin() + 1, vec4.end() - 1); // vec7 包含 {2, 3, 4}

// 7. 创建一个二维vector
std::vector> vec8(3, std::vector(4,0)); //3x4的二维vector,元素初始化为0
```

2.3 访问元素

vector 提供了多种访问元素的方式:

  • 下标运算符 [] 像数组一样,通过索引访问元素。注意:[] 不进行边界检查,如果索引越界,会导致未定义行为。
  • at() 方法: 通过索引访问元素,并进行边界检查。如果索引越界,会抛出 std::out_of_range 异常。
  • front() 方法: 返回 vector 中第一个元素的引用。
  • back() 方法: 返回 vector 中最后一个元素的引用。
  • 迭代器: 使用迭代器遍历 vector 中的元素(稍后详细介绍)。

```c++
std::vector vec = {1, 2, 3, 4, 5};

// 使用 [] 访问
std::cout << vec[0] << std::endl; // 输出 1

// 使用 at() 访问
std::cout << vec.at(2) << std::endl; // 输出 3

// 使用 front() 和 back()
std::cout << vec.front() << std::endl; // 输出 1
std::cout << vec.back() << std::endl; // 输出 5

// 尝试越界访问
// std::cout << vec[10] << std::endl; // 未定义行为
// std::cout << vec.at(10) << std::endl; // 抛出 std::out_of_range 异常
```

2.4 修改元素

```c++
std::vector vec = {1, 2, 3};

// 使用 [] 修改
vec[0] = 10;

// 使用 at() 修改
vec.at(1) = 20;

// vec 现在是 {10, 20, 3}
```

3. Vector 的常用方法

std::vector 提供了丰富的成员函数来操作其内容。

3.1 容量相关

  • size() 返回 vector 中当前元素的个数。
  • empty() 检查 vector 是否为空(即 size() 是否为 0)。
  • capacity() 返回 vector 当前分配的存储空间可以容纳的元素个数。capacity() 通常大于等于 size()
  • reserve(n) 预留至少能容纳 n 个元素的存储空间。这可以减少 vector 在插入元素时重新分配内存的次数,提高效率。注意:reserve() 不会改变 size()
  • shrink_to_fit() (C++11)请求 vector 减少其 capacity() 以匹配 size()。这可以释放多余的内存,但并不保证一定会缩小容量(取决于具体实现)。

```c++
std::vector vec;

std::cout << "Size: " << vec.size() << std::endl; // 输出 0
std::cout << "Capacity: " << vec.capacity() << std::endl; // 输出可能是 0 或一个较小的值

vec.reserve(10);
std::cout << "Size: " << vec.size() << std::endl; // 输出 0
std::cout << "Capacity: " << vec.capacity() << std::endl; // 输出 10 或更大

vec.push_back(1);
vec.push_back(2);

std::cout << "Size: " << vec.size() << std::endl; // 输出 2
std::cout << "Capacity: " << vec.capacity() << std::endl; // 输出 10 或更大

vec.shrink_to_fit(); // 请求缩小容量
std::cout << "Size: " << vec.size() << std::endl; // 输出 2
std::cout << "Capacity: " << vec.capacity() << std::endl; // 可能会缩小,但不一定
```

3.2 元素添加和删除

  • push_back(value)vector 的末尾添加一个值为 value 的元素。
  • pop_back() 删除 vector 的最后一个元素。
  • insert(position, value) 在指定位置 position(迭代器)之前插入一个值为 value 的元素。
  • insert(position, n, value): 在指定位置插入n个值为value的元素。
  • insert(position, first, last): 在指定位置插入范围[first, last)内的元素。
  • erase(position) 删除指定位置 position(迭代器)的元素。
  • erase(first, last) 删除范围 [first, last) 内的元素。
  • clear() 删除 vector 中的所有元素,使其大小变为 0,但不影响capacity。
  • emplace_back(args...): (C++11) 在vector尾部直接构造一个元素,避免了临时对象的创建和拷贝。
  • emplace(position, args...): (C++11) 在指定位置直接构造一个元素。

```c++
std::vector vec = {1, 2, 3};

vec.push_back(4); // vec 现在是 {1, 2, 3, 4}
vec.pop_back(); // vec 现在是 {1, 2, 3}

vec.insert(vec.begin() + 1, 5); // vec 现在是 {1, 5, 2, 3}
vec.insert(vec.begin()+2, 2, 9); // vec 现在是{1,5,9,9,2,3}
std::vector vec2 = {7, 8};
vec.insert(vec.end(), vec2.begin(), vec2.end()); //vec 现在是{1,5,9,9,2,3,7,8}

vec.erase(vec.begin() + 2); // vec 现在是 {1, 5, 9, 2, 3, 7, 8}
vec.erase(vec.begin() + 1, vec.begin() + 3); //vec 现在是{1,2,3,7,8}
vec.clear(); // vec 现在是 {}

// emplace_back 示例
struct MyObject {
int x;
double y;
MyObject(int x, double y) : x(x), y(y) {}
};

std::vector objects;
objects.emplace_back(10, 3.14); // 直接在 vector 中构造 MyObject 对象

```

3.3 其他常用方法

  • resize(n) 调整 vector 的大小为 n。如果 n 小于当前大小,则删除多余的元素;如果 n 大于当前大小,则添加默认初始化的元素。
  • resize(n, value): 调整vector大小为n, 新添加的元素初始化为value.
  • swap(other_vector) 交换两个 vector 的内容。这是一个非常高效的操作,通常是常数时间复杂度。
  • assign(n, value):vector 的内容替换为 n 个值为 value 的元素。
  • assign(first, last): 将vector的内容替换为范围[first,last)内的元素。
  • operator=: 赋值运算符, 将一个vector的内容复制到另一个vector.
  • data(): (C++11) 返回一个指向vector底层数组的指针, 可以用于与C风格的API交互, 但要注意不要越界访问或修改vector的大小.

```c++
std::vector vec = {1, 2, 3};
vec.resize(5); // vec 现在是 {1, 2, 3, 0, 0}
vec.resize(2); // vec 现在是 {1, 2}
vec.resize(4, 9); //vec 现在是 {1,2,9,9}

std::vector vec2 = {4, 5, 6};
vec.swap(vec2); // vec 现在是 {4, 5, 6}, vec2 现在是 {1, 2}

vec.assign(3, 7); //vec 现在是{7,7,7}
std::vector vec3 = {1,2,3,4};
vec.assign(vec3.begin()+1, vec3.end()-1); //vec 现在是{2,3}
```

4. 迭代器

迭代器是一种用于遍历容器中元素的对象,类似于指针。vector 提供了以下几种迭代器:

  • begin() 返回指向 vector 第一个元素的迭代器。
  • end() 返回指向 vector 最后一个元素之后位置的迭代器(这是一个“past-the-end”迭代器,不指向任何有效元素)。
  • rbegin() 返回指向 vector 最后一个元素的反向迭代器。
  • rend() 返回指向 vector 第一个元素之前位置的反向迭代器。
  • cbegin()cend()crbegin()crend() (C++11)返回对应迭代器的 const 版本,用于只读访问。

```c++
std::vector vec = {1, 2, 3, 4, 5};

// 使用正向迭代器遍历
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " "; // 输出 1 2 3 4 5
}
std::cout << std::endl;

// 使用反向迭代器遍历
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
std::cout << *it << " "; // 输出 5 4 3 2 1
}
std::cout << std::endl;

// 使用 const 迭代器遍历 (C++11)
for (auto it = vec.cbegin(); it != vec.cend(); ++it) {
std::cout << it << " "; // 输出 1 2 3 4 5
//
it = 10; // 错误:不能通过 const 迭代器修改元素
}
std::cout << std::endl;
```

迭代器失效问题:

vector进行插入或删除操作(如push_back, insert, erase等)可能会导致迭代器失效。失效的迭代器不能再被使用,否则会导致未定义行为。

一般来说,以下规则可以帮助判断迭代器是否失效:

  • 插入操作:
    • 如果插入操作导致vector重新分配内存(即capacity发生了变化),则所有迭代器、指针和引用都会失效。
    • 如果插入操作没有导致vector重新分配内存,则指向插入位置之后元素的迭代器、指针和引用会失效,但指向插入位置之前元素的迭代器仍然有效。
  • 删除操作:
    • 指向被删除元素的迭代器、指针和引用会失效。
    • 指向被删除元素之后元素的迭代器、指针和引用也会失效。

为了避免迭代器失效问题,可以采取以下措施:

  • 在插入或删除元素后,重新获取迭代器。
  • 如果需要保存迭代器,可以使用整数索引代替,因为索引不会失效。
  • 对于复杂的迭代器操作,可以考虑使用更稳定的容器,如std::liststd::deque

5. Vector 的高级应用

5.1 作为函数参数和返回值

vector 可以作为函数的参数和返回值。通常,建议使用引用传递(&)或 const 引用传递(const &)来避免不必要的复制开销。

```c++
// 通过引用传递,避免复制
void processVector(std::vector& vec) {
// ... 对 vec 进行修改 ...
}

// 通过 const 引用传递,只读访问
void printVector(const std::vector& vec) {
for (int x : vec) {
std::cout << x << " ";
}
std::cout << std::endl;
}

// 返回 vector
std::vector createVector(int n) {
std::vector vec(n);
// ... 初始化 vec ...
return vec;
}
```

5.2 Vector 与算法

vector 可以与 STL 中的各种算法(如 std::sortstd::findstd::transform 等)结合使用,实现强大的功能。

```c++

include

std::vector vec = {5, 2, 8, 1, 9, 4};

// 排序
std::sort(vec.begin(), vec.end()); // vec 现在是 {1, 2, 4, 5, 8, 9}

// 查找
auto it = std::find(vec.begin(), vec.end(), 8);
if (it != vec.end()) {
std::cout << "Found 8 at index: " << std::distance(vec.begin(), it) << std::endl;
}

// 使用 lambda 表达式进行转换
std::transform(vec.begin(), vec.end(), vec.begin(), { return x * 2; });
// vec 现在是 {2, 4, 8, 10, 16, 18}
```

5.3 Vector 存储自定义类型

vector 不仅可以存储内置类型,还可以存储自定义类型(类或结构体)。但要注意,如果自定义类型包含动态分配的资源(如指针),则需要正确实现拷贝构造函数、赋值运算符和析构函数,以避免浅拷贝和内存泄漏问题。 或者使用智能指针。

```c++
struct Person {
std::string name;
int age;

Person(std::string name, int age) : name(name), age(age) {}

};

std::vector people;
people.emplace_back("Alice", 30);
people.emplace_back("Bob", 25);
```

6. Vector 的性能考虑

  • 预分配内存: 如果事先知道 vector 将要存储的元素数量,使用 reserve() 预分配足够的内存可以避免多次内存重新分配,提高效率。
  • 避免不必要的复制: 在传递 vector 时,尽量使用引用或 const 引用。在添加元素时,如果可以,使用 emplace_back()emplace() 代替 push_back()insert() 来避免创建临时对象。
  • 选择合适的插入/删除位置:vector 的末尾插入和删除元素(push_back()pop_back())非常高效(通常是常数时间复杂度)。但在中间或开头插入和删除元素(insert()erase())会导致后续元素移动,效率较低(线性时间复杂度)。如果需要频繁在中间插入和删除元素,可以考虑使用其他容器,如 std::list
  • 使用移动语义: (C++11) 如果元素类型支持移动语义(move semantics),vector 的一些操作(如 push_back()insert()resize() 等)会自动利用移动语义来提高效率。

7. 总结

std::vector 是 C++ 中一个非常强大且常用的容器。通过掌握其基本用法、常用方法、迭代器、高级应用以及性能注意事项,您可以编写出更高效、更健壮的 C++ 代码。vector 的灵活性和高效性使其成为处理动态数组和序列数据的理想选择。

希望这篇详细的指南对您有所帮助! 如果您有任何其他问题,请随时提出。

THE END
icon
0
icon
打赏
icon
分享
icon
二维码
icon
海报
发表评论
评论列表

赶快来坐沙发