C++ 中的向量是如何工作的?
在 C++ 中,向量是一种类似于数组的数据结构,可以存储一个或多个对象。和数组一样,向量也可以使用下标访问任何一个元素,但是它还有一些其他的特性使得它更为方便和灵活。
向量的定义和使用
在 C++ 中,使用标准库中的 vector 类定义和使用向量。向量的定义方式和数组类似,但是需要指定存储的数据类型,例如:
#include <vector>
std::vector<int> v; // 定义一个空的整型向量
std::vector<std::string> strs(10); // 定义一个字符串向量,初始大小为 10
上述代码中,第一行定义了一个空的整型向量 v,第二行定义了一个字符串向量 strs,并指定它的初始大小为 10。需要注意的是,这里使用了 std 命名空间,表示所使用的是标准库中的 vector 类。
向量的使用方式和数组类似,可以使用下标访问任何一个元素:
std::vector<int> v = {1, 2, 3};
std::cout << v[0] << std::endl; // 输出 1
std::cout << v[1] << std::endl; // 输出 2
std::cout << v[2] << std::endl; // 输出 3
上述代码中,定义了一个包含三个元素的整型向量 v,并使用下标分别访问了每个元素。
除此之外,向量还提供了一些其他的方法,使得它更加方便和灵活。比如,可以使用 push_back 方法向向量中添加元素:
std::vector<int> v = {1, 2, 3};
v.push_back(4);
v.push_back(5);
std::cout << v[3] << std::endl; // 输出 4
std::cout << v[4] << std::endl; // 输出 5
上述代码中,调用了 push_back 方法向向量 v 中添加了两个元素,并通过下标访问了这些新添加的元素。
向量的内部实现
向量的内部实现使用了动态数组,因此向量的大小可以根据实际需求动态增加或减少。当向量需要扩展容量时,向量会自动重新分配内存,并将元素复制到新的内存位置。这个过程称为重新分配。
使用以下代码可以查看向量的容量和大小:
std::vector<int> v = {1, 2, 3};
std::cout << v.size() << std::endl; // 输出 3
std::cout << v.capacity() << std::endl; // 输出 4
上述代码中,调用了 size 方法获取向量中元素的个数,调用了 capacity 方法获取向量容量的大小。由于向量的默认容量大小为 0,因此向量 v 的容量为 4。
当向量中的元素个数超过了当前容量时,向量会重新分配内存,容量大小会增加一倍。比如,当向量中的元素个数为 5 时,向量的容量大小会自动增加到 8。这个过程可以使用以下代码进行演示:
std::vector<int> v;
std::cout << v.capacity() << std::endl; // 输出 0
for (int i = 0; i < 5; i++) {
v.push_back(i);
std::cout << v.capacity() << std::endl;
}
上述代码中,定义了一个空的整型向量 v,并循环向向量中添加元素。在每次添加元素后,都输出向量的容量大小。可以看到,向量的容量大小随着元素个数的增加而自动增加。
向量的遍历和排序
向量的遍历方式和数组类似,可以使用 for 循环对每个元素进行访问:
std::vector<int> v = {1, 2, 3};
for (int i = 0; i < v.size(); i++) {
std::cout << v[i] << " ";
}
std::cout << std::endl;
上述代码中,循环遍历了向量 v 中的每个元素并输出。
在 C++ 中,向量可以使用算法库中的 sort 方法进行排序。sort 方法的使用方式和数组类似:
std::vector<int> v = {3, 1, 4, 5, 2};
std::sort(v.begin(), v.end());
for (int i = 0; i < v.size(); i++) {
std::cout << v[i] << " ";
}
std::cout << std::endl;
上述代码中,定义了一个包含 5 个元素的整型向量 v,然后使用 sort 方法将它们排序。最后循环遍历向量并输出排序后的结果。
需要注意的是,sort 方法是一种快速排序算法,算法的时间复杂度为 O(n log n)。对于小规模的向量排序,可以使用 C++ 标准库中的 stable_sort 方法进行稳定排序,时间复杂度也为 O(n log n)。
向量的复制和赋值
向量的复制和赋值和其他数据类型类似,有浅复制和深复制两种方式。
浅复制是指将一个向量的指针或引用赋给另外一个向量,这样两个向量会共享同一个内存空间。当其中一个向量修改了内存中的数据时,另外一个向量也会受到影响。例如:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = v1;
v2[0] = 4;
std::cout << v1[0] << " " << v2[0] << std::endl; // 输出 4 4
上述代码中,定义了一个包含三个元素的整型向量 v1,然后使用 v1 的副本 v2 进行浅复制。修改 v2 中的第一个元素会影响到 v1。
深复制是指将一个向量的每个元素复制到另外一个向量中,两个向量之间没有任何联系。这样做是为了避免两个向量之间的相互影响。然而,由于向量的元素可能是指针或其他引用类型,因此进行深复制时需要谨慎处理。例如:
std::vector<int*> v1;
std::vector<int*> v2;
v1.push_back(new int(1));
v1.push_back(new int(2));
v2 = v1;
*(v2[0]) = 3;
std::cout << *(v1[0]) << " " << *(v2[0]) << std::endl; // 输出 3 3
上述代码中,定义了两个整型指针向量 v1 和 v2,并将两个整数类型的指针添加到 v1 中。然后使用 v1 的副本 v2 进行浅复制,并将 v2 中第一个元素所指向的整数类型的值修改为 3。输出结果显示,两个向量中的值都被修改为 3。
向量的注意事项
在使用向量时,需要注意一些细节问题。以下是一些需要特别注意的事项:
- 向量的下标越界会导致未定义行为,需要特别小心。
- 在插入或删除向量的元素时,迭代器可能会失效,因此需要重新获取迭代器。
- 向量的元素类型需要是可复制的。如果要存储自定义的类型,需要实现元素的复制构造函数和赋值运算符重载函数。
-
在向量中存储指针时,需要注意指针的生命周期,不能出现悬空指针。
综上所述,虽然向量看起来很简单,但在实际使用过程中需要注意一些细节问题,才能保证程序的正确性和稳定性。
结论
C++ 中的向量是一种类似于数组的数据结构,可以存储一个或多个对象。和数组一样,向量也可以使用下标访问任何一个元素,但是它还有一些其他的特性使得它更为方便和灵活。向量的内部实现使用了动态数组,因此向量的大小可以根据实际需求动态增加或减少。 向量可以使用算法库中的 sort 方法进行排序,sort 方法是一种快速排序算法。向量的复制和赋值有浅复制和深复制两种方式,需要注意相关细节问题。