std::vector是C++标准库中一个非常强大的容器类,它提供了动态数组的功能。std::vector可以自动调整大小,提供了随机访问的能力,同时还支持在序列的尾部高效地添加和删除元素。
当创建一个空的std::vector对象时,它不分配任何内存,大小是0。当开始添加元素时,std::vector会动态分配内存。通常情况下,第一次分配的内存大小由具体的实现决定,但通常是一个小的初始值,比如16或32个元素大小的内存块。
当元素数量超过当前内存容量时,std::vector将重新分配内存,并将元素从旧的内存块复制到新的内存块中。新的内存块的大小通常是当前元素数量的两倍或更多,具体取决于具体的实现和策略。重新分配内存时,可能会有一些额外的内存开销,因为需要考虑到内存对齐和内存管理的一些细节。
std::vector的特性:
- 动态大小:std::vector的大小可以动态变化,能够根据需要自动扩容
- 连续存储:像静态数组一样,内部使用连续的内存存储元素,这使得元素访问速度很快
- 模板类:std::vector是一个模板类,可以存储任何类型的数据
std::vector提供了一系列成员函数来操作和管理动态数组:
构造函数和析构函数
std::vector():构造一个空的向量
std::vector(size_type n, const T& value = T()):构造一个包含n个具有value值的元素的向量
std::vector(const std::vector& other):拷贝构造函数,深拷贝,会复制other中的元素,并存储到新的向量中
std::vector(std::vector&& other) noexcept:移动构造函数,将另一个已存在的vector对象的资源移动到新创建的vector中,而不是进行深拷贝
~std::vector():析构函数,释放向量所占用的内存
std::vector<int> vec; // 创建vec向量 std::vector<std::string> vec(5, "A"); // 创建一个向量,该向量有5个元素,每个元素的值都是"A" std::vector<int> vec = {1, 2, 3, 4, 5}; // 创建向量vec,并进行初始化 std::vector<int> new_vec(vec); // 创建新的向量new_vec,并深拷贝vec中的元素 std::vector<int> vec = {1, 2, 3, 4, 5}; std::vector<int> new_vec(std::move(vec)); // 创建新的向量new_vec,并将vec的资源移动到new_vec 在C++标准库中,std::vector的析构函数是隐式生成的,如果用户没有自定义,将会使用编译器默认生成的析构函数。默认情况下,析构函数会按顺序释放元素,然后释放分配的内存。因此,std::vector在其生命周期结束时会自动执行析构函数,确保动态分配的内存被正确释放,从而避免内存泄漏。
元素访问
operator[]:通过索引访问向量的元素
at(value):通过索引访问向量的元素,带边界检查
front():访问向量的第一个元素
back():访问向量的最后一个元素
data():返回一个指向向量第一个元素的指针
std::vector<int> vec{0, 1, 2, 3, 4, 5}; // 创建向量vec,并进行初始化 std::cout << vec[3] << std::endl; // 3 std::cout<< vec.at(3) << std::endl; // 3 std::cout<< vec.front() << std::endl; // 0 std::cout<< vec.back() << std::endl; // 5 int* ptr = vec.data(); for (size_t i = 0; i < vec.size(); ++i) { std::cout << *(ptr + i) << " "; // 0 1 2 3 4 5 std::cout << ptr[i] << " "; // 0 1 2 3 4 5 }
修改器
push_back(const T& value):在向量的末尾添加一个元素
pop_back():移除向量的最后一个元素
emplace_back(Args&&… args):就地构造一个元素,避免额外的复制或移动
insert(const_iterator pos, const T& value):在指定位置插入一个元素
erase(const_iterator pos):删除指定位置或指定范围内的元素
resize():调整向量的大小
clear():清除向量中的所有元素
std::vector<std::string> vec{"A", "B", "C"}; for (auto value : vec) { std::cout << value << std::endl; // A B C } vec.push_back("D"); // 在向量末尾添加一个元素 for (auto value : vec) { std::cout << value << std::endl; // A B C D } vec.pop_back(); // 移除向量的最后一个元素 for (auto value : vec) { std::cout << value << std::endl; // A B C } vec.emplace_back("E"); // 在向量末尾添加一个元素 for (auto value : vec) { std::cout << value << std::endl; // A B C E } vec.insert(vec.begin() + 1, "F"); // 在索引为1的位置插入元素"F" for (auto value : vec) { std::cout << value << std::endl; // A F B C E } std::string values[] = {"X", "Y", "Z"}; vec.insert(vec.end(), std::begin(values), std::end(values)); // 在向量末尾插入一系列元素 for (auto value : vec) { std::cout << value << std::endl; // A F B C E X Y Z } vec.erase(vec.begin() + 1); // 删除索引为1的元素 for (auto value : vec) { std::cout << value << std::endl; // A B C E X Y Z } vec.erase(vec.begin() + 4, vec.end()); // 删除一系列元素,范围包括索引为4的元素到最后一个元素;范围是左闭右开,vec.end()指向末尾元素的后一位,因此囊括了末尾元素 for (auto value : vec) { std::cout << value << std::endl; // A B C E } vec.resize(3); // 调整向量大小,新的向量变小了,会删除多余的元素;A B C vec.resize(5); // 调整向量大小,新的向量变大了,会添加新的元素,默认构造;A B C 空值 空值 vec.resize(7, "E"); // 调整向量大小,新的向量变大了,会添加指定了值的元素;A B C 空值 空值 E E vec.clear(); // 清除向量中的所有元素;向量变为空,但分配的内存不会被释放 std::cout << vec.size() << std::endl; // 0 /* push_back函数用于在向量的末尾添加一个元素,这个元素可以是值,也可以是另一个向量中元素的引用或指针。 emplace_back函数用于在向量的末尾就地构造一个元素,它可以避免临时对象的创建,而是直接将参数传递给构造函数,从而提高效率。 emplace_back通常比push_back更高效,因为它避免了复制或移动操作,直接在向量的末尾就地构造元素。 如果需要构造一个对象并将其添加到向量的末尾,emplace_back是更好的选择;如果需要添加一个已经存在的对象,push_back是合适的。 */
容量
empty():判断向量是否为空,返回一个布尔值
size():返回向量中元素的数量
max_size():返回向量能容纳的最大元素数量
capacity():返回向量当前的容量
reserve(size_type new_cap):改变向量的容量
shrink_to_fit():减小向量的容量以适应其大小
std::vector<int> vec = {1, 2, 3, 4, 5}; bool is_empty = vec.empty(); // 如果向量为空,返回1;如果向量不为空,返回0 std::cout << is_empty << std::endl; // 输出0 vec.pop_back(); // 移除末尾元素 std::cout << vec.size() << std::endl; // 输出4,向量中还有4个元素 std::cout << vec.capacity() << std::endl; // 输出5,虽然移除了一个元素,但占用的内存空间并没有释放,因此容量为5 std::cout << vec.max_size() << std::endl; // 4611686018427387903,向量能容纳的最大元素数量 vec.reserve(10); // 将vec的容量改为10 std::cout << vec.capacity() << std::endl; // 输出10 vec.shrink_to_fit(); // 改变容量以适应向量的大小 std::cout << vec.capacity() << std::endl; // 输出4,因为向量中有4个元素,因此容量会自动适应向量的大小
迭代器
begin():返回指向向量第一个元素的迭代器
end():返回指向向量尾后一位的迭代器
cbegin():返回指向向量第一个元素的const迭代器
cend():返回指向向量尾后一位的const迭代器
rbegin():返回指向向量最后一个元素的反向迭代器
rend():返回指向向量首前一位的反向迭代器
crbegin():返回指向向量最后一个元素的const反向迭代器
crend(): 返回指向向量首前一位的const反向迭代器
std::vector<int> vec{1, 2, 3, 4, 5}; for (auto it = vec.begin(); it != vec.end(); it++) { std::cout << *it << std::endl; // 1 2 3 4 5 } for (auto it = vec.cbegin(); it != vec.cend(); it++) { std::cout << *it << std::endl; // 1 2 3 4 5 } for (auto it = vec.rbegin(); it != vec.rend(); it++) { std::cout << *it << std::endl; // 5 4 3 2 1 } for (auto it = vec.crbegin(); it != vec.crend(); it++) { std::cout << *it << std::endl; // 5 4 3 2 1 }
数据操作/赋值操作
assign(size_type n, const T& value):替换向量的内容为n个value元素
swap(std::vector& other):交换向量的内容
std::vector<int> vec = {1, 2, 3, 4, 5}; std::vector<int> new_vec = {6, 7, 8, 9}; vec.assign(new_vec.begin(), new_vec.end()); // 将new_vec的全部元素赋给vec for (auto value : vec) { std::cout << value << std::endl; // 6 7 8 9 } vec.assign(5, 10); // 将5个10赋给vec for (auto value : vec) { std::cout << value << std::endl; // 10 10 10 10 10 }
std::vector<int> vec = {1, 2, 3, 4, 5}; std::vector<int> new_vec = {6, 7, 8, 9}; vec.swap(new_vec); // 将vec与new_vec的元素互相交换 for (auto value : vec) { std::cout << value << std::endl; // 6 7 8 9 } for (auto value : new_vec) { std::cout << value << std::endl; // 1 2 3 4 5 }
查找操作
find(const T& value):查找第一个等于value的元素
lower_bound(const T& value):查找第一个不小于value的元素
upper_bound(const T& value):查找第一个大于value的元素
std::vector<int> vec{3, 2, 1, 4, 5}; auto it = std::find(vec.begin(), vec.end(), 3); // std::find函数用于在向量中查找特定值,并返回指向找到的元素的迭代器,如果未找到,则返回尾后迭代器 std::cout << *it << std::endl; // 输出3 auto it = std::find(vec.begin(), vec.end(), 0); // 要查找的0不在向量中,因此返回尾后迭代器,即指向向量尾后一位的迭代器 std::cout << *(it - 1) << std::endl; // 输出5,尾后迭代器前一个元素就是向量的末尾元素
std::vector<int> vec{3, 2, 1, 4, 5}; std::sort(vec.begin(), vec.end()); // 对向量进行排序,默认为升序 for (auto value : vec) { std::cout << value << std::endl; // 1 2 3 4 5 } std::sort(vec.begin(), vec.end(), std::greater<int>()); // 对向量进行降序排序 for (auto value : vec) { std::cout << value << std::endl; // 5 4 3 2 1 } std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; }); // 使用lambda表达式实现降序排序 for (auto value : vec) { std::cout << value << std::endl; // 5 4 3 2 1 } /* std::sort是一个非常强大的标准库算法,用于对序列进行排序。它可以对数组、向量(std::vector)、字符串(std::string)等容器进行排序。std::sort函数定义在<algorithm>头文件中。 */
std::vector<int> vec{3, 4, 6, 7, 8}; auto it = std::lower_bound(vec.begin(), vec.end(), 4); // 找出第一个不小于4的元素,并返回指向该元素的迭代器 std::cout << *it << std::endl; // 输出4 auto it = std::lower_bound(vec.begin(), vec.end(), 5); // 找出第一个不小于5的元素,并返回指向该元素的迭代器 std::cout << *it << std::endl; // 输出6 auto it = std::upper_bound(vec.begin(), vec.end(), 4); // 找出第一个大于4的元素,并返回指向该元素的迭代器 std::cout << *it << std::endl; // 输出6
运算符重载
赋值运算符 =:用于将一个向量的内容赋值给另一个向量
比较运算符 ==、!=、<、>、<=、>=:用于比较两个向量的内容
std::vector<int> vec1 = {1, 2, 3}; std::vector<int> vec2; vec2 = vec1; // 将vec1的内容赋给vec2 for (auto value : vec2) { std::cout << value << std::endl; // 1 2 3 }
std::vector<int> vec1 = {1, 2, 3}; std::vector<int> vec2 = {1, 2, 4}; bool res = vec1 <= vec2; std::cout << res << std::endl; // 输出1
特殊操作
emplace(const_iterator pos, Args&&… args):在指定位置就地构造一个元素,它可以直接将参数传给构造函数,而不需要提前创建一个对象
std::vector<int> vec = {1, 2, 3, 4, 5}; vec.emplace(vec.begin(), 0); // 在vec的第一个位置就地插入一个元素0 for (auto value : vec) { std::cout << value << std::endl; // 0 1 2 3 4 5 }