C++ STL(一)std::vector std::array
目录
std::vector
1、vector是什么
2、使用详解
构造函数
元素访问
容量
resize与reserve
修改
迭代器
迭代器失效情况
code举例
3、扩展
push_back() 和 emplace_back() 的区别
std::array
构造函数
元素访问
容量
修改
迭代器
code举例
和vector相比
std::vector
1、vector是什么
C++ 中的 vector 是标准模板库(STL)提供的一个动态数组容器。它的设计使得在使用时能够高效地管理内存和元素。
template <typename T>
class vector {
private:
T* data; // 指向动态数组的指针
size_t size; // 当前元素数量
size_t capacity; // 当前容量
};
内部结构 | 指针:指向动态分配的数组的指针。大小(size):当前 vector 中元素的数量。 容量(capacity):当前 vector 分配的存储空间的大小,表示可以容纳的元素数量,而不需要重新分配内存。 |
内存管理 | vector 使用动态内存分配来存储元素。初始时vector 的容量可能为零或一个小的固定值。当添加元素时,如果当前的大小超过了容量,vector 会进行扩展 |
动态扩展机制 | 当vector 的大小超过其容量时: 分配新内存:通常会分配比当前容量大一倍的新内存,以减少频繁的内存分配和拷贝操作。 |
元素访问 | 随机访问:可以通过下标操作符 [] 或 at() 方法快速访问任意位置的元素,时间复杂度为 O(1)。 前后元素:可以使用 front() 和 back() 方法访问第一个和最后一个元素。 |
迭代器支持 | vector 支持迭代器,使得可以使用 STL 算法和范围基于的 for 循环。begin() 和 end() 方法返回指向 vector 开始和结束的迭代器 |
线程安全性 | vector 本身并不是线程安全的。如果多个线程同时访问同一个 vector,需要使用适当的同步机制(如互斥锁)来保证安全性。 |
2、使用详解
构造函数
vector() | 默认构造函数,创建一个空的 vector |
vector(size_type n) | 创建一个包含 n 个默认初始化元素的 vector |
vector(size_type n, const T& value) | 创建一个包含 n 个值为 value 的元素的 vector |
vector(const vector& other): | 拷贝构造函数,创建一个与 other 相同的 vector |
vector(initializer_list<T> init): | 使用初始化列表创建 vector |
元素访问
at(size_type pos) | 返回指定位置 pos 处的元素的引用,并进行边界检查。 |
operator[](size_type pos) | 返回指定位置 pos 处的元素的引用,不进行边界检查。 |
front() | 返回第一个元素的引用。 |
back() | 返回最后一个元素的引用。 |
data() | 返回指向底层数组的指针。 |
容量
empty(): 检查 vector 是否为空。 size(): 返回 vector 中的元素数量。max_size(): 返回 vector 可以容纳的最大元素数量。capacity(): 返回 vector 当前分配的存储容量。reserve(size_type new_cap): 增加 vector 的容量到至少 new_cap。 shrink_to_fit(): 请求移除未使用的容量。
resize与reserve
resize(int num); 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem); 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长>度的元素被删除。
reserve(int len); 容器预留len个元素长度,预留位置不初始化,元素不可访问。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// 初始状态
std::cout << "Initial state:\n";
std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << "\n\n";
// 使用 reserve 预分配容量
vec.reserve(5);
std::cout << "After reserve(5):\n";
std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << "\n\n";
// 使用 resize 改变大小
vec.resize(3);
std::cout << "After resize(3):\n";
std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << "\n";
std::cout << "Elements: ";
for (int i : vec) {
std::cout << i << " "; // 默认初始化为 0
}
std::cout << "\n\n";
// 再次使用 resize 增加大小
vec.resize(6, 42); // 新元素初始化为 42
std::cout << "After resize(6, 42):\n";
std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << "\n";
std::cout << "Elements: ";
for (int i : vec) {
std::cout << i << " ";
}
std::cout << "\n\n";
// 再次使用 reserve 增加容量
vec.reserve(10);
std::cout << "After reserve(10):\n";
std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << "\n";
std::cout << "Elements: ";
for (int i : vec) {
std::cout << i << " ";
}
std::cout << "\n\n";
// 使用 resize 减小大小
vec.resize(2);
std::cout << "After resize(2):\n";
std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << "\n";
std::cout << "Elements: ";
for (int i : vec) {
std::cout << i << " ";
}
std::cout << "\n\n";
return 0;
}
修改
clear(): 清除 vector 中的所有元素 | pop_back(): 删除 vector 的最后一个元素 |
erase(iterator pos): 删除指定位置 pos 处的元素 | resize(size_type count): 改变 vector 的大小为 count |
push_back(const T& value): 在 vector 的末尾添加元素 value | swap(vector& other): 交换两个 vector 的内容 |
insert(iterator pos, const T& value): 在指定位置 pos 插入元素 value |
迭代器
begin(): 返回指向第一个元素的迭代器。 | |
end(): 返回指向最后一个元素之后位置的迭代器。 | |
rbegin(): 返回指向最后一个元素的反向迭代器。 | |
rend(): 返回指向第一个元素之前位置的反向迭代器 | |
cbegin(), cend(), crbegin(), crend(): 返回常量迭代器。 |
迭代器失效情况
操作 | 失效的迭代器 |
所有只读操作 | 决不失效 at()、front()、back() |
swap、std::swap | end() 失效 (指向的位置可能改变) |
clear、operator=、assign | 所有迭代器始终失效 |
reserve、shrink_to_fit | 当 vector 更改容量时,所有迭代器失效;否则不失效 |
erase | 被擦除元素及其后所有元素(包括 end() )失效 |
push_back、emplace_back | 当 vector 更改容量时,所有迭代器失效;否则只有 end() 失效 |
insert、emplace | 当 vector 更改容量时,所有迭代器失效;否则插入点后及 end() 失效 |
resize | 当 vector 更改容量时,所有迭代器失效;否则只有 end() 和被擦除元素失效 |
pop_back | 指向被删除元素的迭代器和 end() 迭代器会失效。 |
code举例
#include <vector>
#include <iostream>
int main() {
//构造函数初始化
std::cout<<"----construct-----"<<std::endl;
std::vector<int> v1; // 空vector
std::vector<int> v2(5); // 包含5个默认初始化元素的vector
std::vector<int> v3(5, 10); // 包含5个值为10的元素的vector
std::vector<int> v = {1, 2, 3, 4, 5}; // 使用初始化列表创建vector
//元素访问
std::cout<<"----element get-----"<<std::endl;
std::cout << "v[2]: " << v[2] << std::endl; // 输出: 3
std::cout << "v.at(3): " << v.at(3) << std::endl; // 输出: 4
std::cout << "v.front(): " << v.front() << std::endl; // 输出: 1
std::cout << "v.back(): " << v.back() << std::endl; // 输出: 5
int* p = v.data();
std::cout <<v.capacity()<<std::endl;
std::cout << "v.data()[1]: " << p[1] << std::endl; // 输出: 2
//std::cout << "v[12]: " << v[12] << std::endl; // 可能导致运行时崩溃
//std::cout << "v.at(13): " << v.at(13) << std::endl; // terminate called after throwing
//容量
std::cout<<"----capacity-----"<<std::endl;
std::cout << "v.empty(): " << v.empty() << std::endl; // 输出: 0 (false)
std::cout << "v.size(): " << v.size() << std::endl; // 输出: 5
std::cout << "v.capacity(): " << v.capacity() << std::endl; // 输出: 5
v.reserve(10);
std::cout << "v.capacity() after reserve: " << v.capacity() << std::endl; // 输出: 10
v.shrink_to_fit();
std::cout << "v.capacity() after shrink_to_fit: " << v.capacity() << std::endl; // 输出: 5
//修改
std::cout<<"----modify-----"<<std::endl;
v.push_back(6); // v: {1, 2, 3, 4, 5, 6}
v.pop_back(); // v: {1, 2, 3, 4, 5}
v.insert(v.begin() + 2, 10); // v: {1, 2, 10, 3, 4, 5}
v.erase(v.begin() + 3); // v: {1, 2, 10, 4, 5}
v.resize(3); // v: {1, 2, 10}
v.clear(); // v: {}
std::vector<int> vt= {7, 8, 9};
v.swap(vt); // v: {7, 8, 9}, v2: {}
//迭代器
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
std::cout << "v (reverse): ";
for (auto it = v.rbegin(); it != v.rend(); ++it) {
std::cout << *it << " ";
}
return 0;
}
3、扩展
push_back() 和 emplace_back() 的区别
push_back():用于将一个对象添加到 vector 的末尾。需要传入一个已经存在的对象(或对象的副本),会调用该对象的拷贝构造函数(或移动构造函数)。如果传入的是临时对象push_back() 会先创建临时对象,然后再拷贝到 vector 中。
emplace_back():用于在 vector 的末尾直接构造一个对象。接受构造对象所需的参数并在 vector内部直接构造对象,避免了不必要的拷贝或移动。适用于需要传递多个参数的构造函数。
#include <iostream>
#include <vector>
#include <string>
class Person {
public:
std::string name;
int age;
// 带参数的构造函数
Person(const std::string& name, int age) : name(name), age(age) {
std::cout << "Parameterized constructor called: " << name << ", " << age << std::endl;
}
// 拷贝构造函数
Person(const Person& other) : name(other.name), age(other.age) {
std::cout << "Copy constructor called for: " << name << std::endl;
}
// 赋值构造函数
Person& operator=(const Person& other) {
if (this != &other) {
name = other.name;
age = other.age;
std::cout << "Assignment operator called for: " << name << std::endl;
}
return *this;
}
};
int main() {
std::vector<Person> people;
// 使用 emplace_back()
// people.emplace_back("Alice", 25); // 直接在 vector 中构造对象
// // 通过赋值构造函数验证
// Person bob("Bob", 22);
// people.push_back(bob); // 调用拷贝构造函数
//输出结果可能为
/*Parameterized constructor called: Alice, 25
Parameterized constructor called: Bob, 22
Copy constructor called for: Bob
Copy constructor called for: Alice*/
//因为始时,people 的容量可能为 0 或 1。当执行 people.emplace_back("Alice", 25); 时,
//vector 可能会分配一个初始容量(例如 1)。当执行 people.push_back(bob); 时,vector
//的容量可能不足以容纳第二个元素,因此会触发扩容操作。扩容时,vector 会分配一个新的更大的内存块。
//然后将现有元素(即 Alice)从旧内存拷贝到新内存中。
people.reserve(4); //预先分配大空间
people.emplace_back("Alice", 25); // 直接在 vector 中构造对象
// 通过赋值构造函数验证
Person bob("Bob", 22);
people.push_back(bob); // 调用拷贝构造函数
//可以证明调用了拷贝构造函数
// Parameterized constructor called: Alice, 25
// Parameterized constructor called: Bob, 22
// Copy constructor called for: Bob
return 0;
}
std::array
std::array 的底层实现是一个固定大小的数组,无法动态调整,其大小在编译时确定。它是对 C 风格数组的封装,提供了 STL 容器的接口。内存连续,支持高效的随机访问。类型安全,避免了 C 风格数组的隐式指针转换。提供了 STL 容器的接口(如迭代器、size() 等)。
template <typename T, std::size_t N>
struct array {
T _M_elems[N]; // 底层数组
};
构造函数
array(): | 默认构造函数 |
array(const array& other) | 拷贝构造函数 |
array(initializer_list<T> init) | 使用初始化列表创建 array |
元素访问
front(): 返回第一个元素的引用 | back(): 返回最后一个元素的引用 |
at(size_type pos): 返回指定位置 pos 处的元素的引用,并进行边界检查 | data(): 返回指向底层数组的指针 |
operator[](size_type pos): 返回指定位置 pos 处的元素的引用,不进行边界检查 |
容量
empty():判空。 size(): 求元素数量。 max_size(): 可以容纳的最大元素数量(与 size() 相同)
修改
fill(const T& value): 将 array 中的所有元素设置为value
swap(array& other): 交换两个 array 的内容
迭代器
begin(), end(): 返回指向第一个元素和最后一个元素之后位置的迭代器。 | |
rbegin(), rend(): 返回反向迭代器 | |
cbegin(), cend(), crbegin(), crend(): 返回常量迭代器 |
code举例
#include <array>
#include <iostream>
int main() {
//构造函数
std::array<int, 5> a1; // 未初始化的 array
std::array<int, 5> a = {1, 2, 3, 4, 5}; // 使用初始化列表创建 array
//元素访问
std::cout << "a[2]: " << a[2] << std::endl; // 输出: 3
std::cout << "a.at(3): " << a.at(3) << std::endl; // 输出: 4
std::cout << "a.front(): " << a.front() << std::endl; // 输出: 1
std::cout << "a.back(): " << a.back() << std::endl; // 输出: 5
int* p = a.data();
std::cout << "a.data()[1]: " << p[1] << std::endl; // 输出: 2
//容量
std::cout << "a.empty(): " << a.empty() << std::endl; // 输出: 0 (false)
std::cout << "a.size(): " << a.size() << std::endl; // 输出: 5
std::cout << "a.max_size(): " << a.max_size() << std::endl; // 输出: 5
std::array<int, 5> a3 = {1, 2, 3, 4, 5};
std::array<int, 5> a2 = {6, 7, 8, 9, 10};
//修改
a3.fill(0); // a1: {0, 0, 0, 0, 0}
a3.swap(a2); // a1: {6, 7, 8, 9, 10}, a2: {0, 0, 0, 0, 0}
//迭代器
for (auto it = a.begin(); it != a.end(); ++it) {
std::cout << *it << " "; // 输出: 1 2 3 4 5
}
std::cout << std::endl;
std::cout << "a (reverse): ";
for (auto it = a.rbegin(); it != a.rend(); ++it) {
std::cout << *it << " "; // 输出: 5 4 3 2 1
}
return 0;
}
和vector相比
特性 | std::array | std::vector |
---|---|---|
大小 | 固定大小,编译时确定 | 动态大小,可以在运行时改变 |
内存分配 | 通常在栈上分配 | 在堆上动态分配 |
性能 | 更高效,避免动态分配开销 | 在扩展时可能会有性能损失 |
安全性 | 支持范围检查 | 也支持范围检查 |
灵活性 | 不支持动态扩展 | 支持动态扩展和缩减 |
使用场景 | 适合大小固定的场景 | 适合大小不确定或频繁变化的场景 |
std::array与标准库算法兼容:std::array 支持标准库的算法(如 std::sort、std::find 等),可以像其他 STL 容器一样使用。迭代器支持:std::array 提供了 begin() 和 end() 方法,可以方便地与 STL 算法配合使用。
赋值与初始化:std::array 支持直接赋值操作,可以方便地将一个 std::array 赋值给另一个。例如:
std::array<int, 3> arr1 = {1, 2, 3}; std::array<int, 3> arr2 = arr1; // 直接赋值。