当前位置: 首页 > article >正文

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 的大小超过其容量时:

分配新内存:通常会分配比当前容量大一倍的新内存,以减少频繁的内存分配和拷贝操作。
拷贝元素:将原有元素从旧的内存位置拷贝到新的内存位置。
释放旧内存:释放旧的内存空间。
更新指针和容量:更新指向新内存的指针,并设置新的容量。
这种扩展机制使得 vector 在平均情况下对元素的插入操作是常数时间复杂度 O(1),但在最坏情况下(即需要扩展时)是 O(n)

元素访问随机访问:可以通过下标操作符 [] 或 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 的末尾添加元素 valueswap(vector& other): 交换两个 vector 的内容
insert(iterator pos, const T& value):  在指定位置 pos 插入元素 value

迭代器

begin():  返回指向第一个元素的迭代器。
end():   返回指向最后一个元素之后位置的迭代器。
rbegin(): 返回指向最后一个元素的反向迭代器。
rend():   返回指向第一个元素之前位置的反向迭代器
cbegin(), cend(), crbegin(), crend(): 返回常量迭代器。
迭代器失效情况
操作失效的迭代器
所有只读操作决不失效  at()、front()、back()
swap、std::swapend() 失效 (指向的位置可能改变)
clear、operator=、assign所有迭代器始终失效
reserve、shrink_to_fitvector 更改容量时,所有迭代器失效;否则不失效
erase被擦除元素及其后所有元素(包括 end())失效
push_back、emplace_backvector 更改容量时,所有迭代器失效;否则只有 end() 失效
insert、emplacevector 更改容量时,所有迭代器失效;否则插入点后及 end() 失效
resizevector 更改容量时,所有迭代器失效;否则只有 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::arraystd::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; // 直接赋值。


http://www.kler.cn/a/563593.html

相关文章:

  • 玩机日记 15 使用Lucky申请半永久的ssl证书,并且替换阿里云的自签名证书
  • 集合与反射
  • 算法平台的开源框架
  • PDF处理控件Aspose.PDF教程:使用 Python 将 PDF 转换为 TIFF
  • 网页五子棋——项目测试
  • HarmonyOS Design 介绍
  • 基于ffmpeg+openGL ES实现的视频编辑工具-添加背景音乐(十)
  • vue+element-dialog:修改关闭icon / 遮罩层不能挡住弹窗 / 遮罩层不能遮挡元素
  • miqiu的分布式(三):JVM本地锁失效的三大典型场景
  • Java集合设计模式面试题
  • 5 分钟用满血 DeepSeek R1 搭建个人 AI 知识库(含本地部署)
  • 十一、大数据治理平台总体功能架构
  • leetcode 541. 反转字符串 II 简单
  • Document对象
  • docker 安装 seafile 企业云盘
  • flask 是如何分发请求的?
  • PHP-create_function
  • 【linux配置】 修改内核网络参数
  • Unity基础——世界坐标系(Global)和本地坐标系(Local)
  • 安装react报错