📅  最后修改于: 2023-12-03 14:50:41.698000             🧑  作者: Mango
在C++ STL中,向量(vector)是一个广泛使用的容器,常用于存储大量数据并实现动态大小调整。虽然向量看起来很简单,但在实践中,有一些细微的差别可能会影响程序的性能、正确性和可读性。
本文将介绍向量STL中的常见细微差别,让开发者更好地了解和使用向量。
向量的大小可以使用size()
函数获取,也可以使用capacity()
函数获取其分配的存储空间大小。但需要注意的是,向量的大小和容量两者并非一样的概念。
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
cout << "Initial size of vector: " << v.size() << endl;
cout << "Initial capacity of vector: " << v.capacity() << endl;
for (int i = 1; i <= 5; i++) {
v.push_back(i);
cout << "Size of vector after inserting " << i << " elements: " << v.size() << endl;
cout << "Capacity of vector after inserting " << i << " elements: " << v.capacity() << endl;
}
return 0;
}
上述代码输出结果为:
Initial size of vector: 0
Initial capacity of vector: 0
Size of vector after inserting 1 elements: 1
Capacity of vector after inserting 1 elements: 1
Size of vector after inserting 2 elements: 2
Capacity of vector after inserting 2 elements: 2
Size of vector after inserting 3 elements: 3
Capacity of vector after inserting 3 elements: 4
Size of vector after inserting 4 elements: 4
Capacity of vector after inserting 4 elements: 4
Size of vector after inserting 5 elements: 5
Capacity of vector after inserting 5 elements: 8
可以看到,向量在不断插入元素后,其容器大小每次都会增加1,但容量并非每次都增加1,而是遵循一定的策略进行动态分配的。当容器不足以容纳所有元素时,向量会自动重新分配更大的内存空间(通常是当前容量的两倍)。
向量可以通过以下方式进行初始化:
vector<int> v1 = {1, 2, 3};
vector<string> v2 = {"hello", "world"};
vector<int> v3(5);
vector<char> v4(10, 'a');
vector<int> v5(v4);
需要注意的是,大括号初始化方法仅适用于C++11及以后的标准。复制初始化比直接初始化更耗时,因为它需要将一个向量复制到另一个向量中。
向量是一个连续存储的序列,默认情况下,在向量的末尾添加或删除元素的时间复杂度为常数级别(O(1));但在其它位置添加或删除元素则需要进行移动操作,时间复杂度为线性级别(O(n))。
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
v.insert(v.begin()+2, 6);
v.erase(v.begin()+3);
for (auto i : v) {
cout << i << " ";
}
cout << endl;
return 0;
}
上述代码输出结果为:
1 2 6 4 5
向量的迭代器是指向向量元素的指针。有以下几种迭代器类型:
begin()函数返回指向第一个元素的迭代器,end()函数返回指向最后一个元素的下一个位置的迭代器。
vector<int> v = {1, 2, 3, 4, 5};
for (auto i = v.begin(); i != v.end(); i++) {
cout << *i << " ";
}
cout << endl;
输出结果为:
1 2 3 4 5
rbegin()函数返回指向最后一个元素的迭代器,rend()函数返回指向第一个元素的前一个位置的迭代器。
vector<int> v = {1, 2, 3, 4, 5};
for (auto i = v.rbegin(); i != v.rend(); i++) {
cout << *i << " ";
}
cout << endl;
输出结果为:
5 4 3 2 1
cbegin()函数与begin()函数功能相同,但是返回的是常量迭代器。cend()函数与end()函数功能相同,也返回常量迭代器。
crbegin()函数与rbegin()函数功能相同,但返回的是常量迭代器。crend()函数与rend()函数功能相同,也返回常量迭代器。
需要注意的是,const_iterator类型的迭代器只能进行读取操作,不能进行修改操作。
向量的内存管理实际上由其分配器(allocator)实现。向量默认使用std::allocator作为分配器,它在C++标准库中已经预定义。在实际开发中,可以根据需要使用自定义的分配器。
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
template<typename T>
class my_allocator : public std::allocator<T> {
public:
T* allocate(size_t n) {
cout << "Allocating " << n * sizeof(T) << " bytes" << endl;
return std::allocator<T>::allocate(n);
}
void deallocate(T* p, size_t n) {
cout << "Deallocating " << n * sizeof(T) << " bytes" << endl;
std::allocator<T>::deallocate(p, n);
}
};
int main() {
vector<int, my_allocator<int>> v;
v.push_back(1);
v.push_back(2);
return 0;
}
上述代码输出结果为:
Allocating 16 bytes
Allocating 32 bytes
Deallocating 16 bytes
可以看到,自定义分配器my_allocator中的allocate()和deallocate()函数被调用了两次,分别用于向量v的两次尝试插入元素。这表明自定义分配器已经被成功地使用。
本文介绍了向量STL中的常见细微差别,包括容器大小、向量的初始化、向量的存储、向量的迭代器和向量的内存管理等方面。这些差别并非单独存在,它们相互关联、相互影响,共同构建了向量这一强大的工具。开发者可以根据自己的实际需求,选择最适合自己项目的使用方式。