cpp基础知识

275 阅读27分钟

Cpp版本特性

版本特性

C++11、C++14和C++17等新标准引入了许多令人兴奋的新特性,这些特性不仅提高了编程效率,还增强了代码的安全性和可维护性。以下是对这些新特性的详细介绍:

C++11新特性

  1. 自动类型推导(auto关键字) :允许编译器根据初始化表达式自动推导变量的类型,简化了变量声明过程。
  2. 范围for循环:提供了一种更简洁、易读的方式来遍历容器或数组中的元素。
  3. Lambda表达式:支持定义匿名函数,使函数定义更加灵活,便于实现回调函数和函数式编程。
  4. 右值引用和移动语义:通过引入右值引用(&&)和移动构造函数/赋值运算符,实现了资源的高效转移,避免了不必要的拷贝操作,从而提高了性能。
  5. 智能指针:如std::shared_ptrstd::unique_ptr,提供了自动内存管理机制,减少了内存泄漏的风险。
  6. nullptr关键字:替代了传统的NULL宏,提供了一个更明确的空指针常量表示。
  7. 静态断言(static_assert) :允许在编译时进行断言检查,有助于提前发现潜在的错误。
  8. 线程支持库:直接支持多线程编程,包括线程管理、同步机制等,简化了多线程应用的开发过程。

并发支持

  • 引入了新的内存模型,即C++11内存模型,定义了多线程并发环境下对共享数据的访问和修改行为。
    • 提供了对原子类型和无锁编程的支持。
    • 引入了线程库,包括threadmutexcondition_variable等,使C++对线程和锁级别编程的支持更加原生。
  1. auto和decltype
    • auto关键字用于支持自动类型推导,使得声明变量时无需显式指定类型。
    • decltype用于查询表达式的类型。
  2. 期值(future)
    • 提供了futurepromisepackaged_task等机制,用于异步编程和并发控制。
  3. 范围for循环
    • 简化了遍历容器或数组的语法。
  4. 智能指针
    • 引入了unique_ptrshared_ptrweak_ptr等智能指针,用于自动管理内存,减少内存泄漏的风险。
  5. [lambda表达式]:
    • 允许定义匿名函数,并可以捕获外部变量,极大地方便了编程。

C++14新特性

  1. Lambda初始化捕获:允许在lambda表达式的捕获列表中直接初始化捕获的变量,使lambda表达式更加灵活。
  2. 泛型lambda参数:通过使用auto关键字,lambda参数可以自动推导类型,增强了lambda的通用性。
  3. constexpr函数的增强:在C++11的基础上,放松了对constexpr函数的限制,允许其函数体内使用更复杂的控制流结构和局部变量声明。
  4. 废弃标志([[deprecated]]) :引入了一个标准化的属性来标记已弃用的函数、类或其他声明,以便编译器在编译时发出警告。

Lambda初始化捕获: - 在C++14中,lambda表达式的捕获列表支持直接初始化捕获的变量,这意味着可以在定义lambda时直接为捕获的变量赋予初始值。

泛型Lambda参数: - Lambda函数的参数可以使用auto关键字进行自动类型推导,增强了Lambda的灵活性和通用性。

constexpr函数的增强: - 在C++14中,constexpr函数得到了增强,允许在其函数体内使用更复杂的控制流结构和局部变量声明,只要最终的结果能够在编译时计算得出即可。

[[deprecated]]属性: - 引入了一个标准化的属性来标记已弃用的函数、类或其他声明,有助于编译器在编译时发出警告。

返回类型推导: - C++14为所有函数提供了返回类型推导的能力,函数声明必须将auto作为返回类型,且函数体中多个return表达式必须可以推断为相同的类型。

C++17新特性

  1. 结构化绑定:允许将结构体、数组、元组等的成员解构到独立的变量中,简化了复杂数据结构的处理过程。
  2. if constexpr:一个编译时条件判断语句,根据条件编译代码块,对于模板元编程和条件编译非常有用。
  3. std::optional:一个标准库类型,用于表示一个可能包含值也可能不包含值的对象,提供了更安全的空值处理方式。

结构化绑定: - 允许将结构体、数组、元组等的成员解构到独立的变量中,简化了代码书写并提高了可读性。

if constexpr: - 这是一个编译时条件判断,可以根据条件编译代码块,对于模板元编程非常有用。

std::optional: - 这是一个标准库类型,用于表示一个可能包含值也可能不包含值的对象,在需要返回可能无效的结果时非常有用。

折叠表达式: - 允许在模板元编程和参数包展开中使用更简洁的语法。

并行STL: - C++17开始支持并行算法,可以更方便地利用多核处理器进行并行计算。

C++20引入了多项新特性,增强了语言的功能和易用性。以下是一些主要的C++20特性:

  1. 概念(Concepts) :这是使用模板进行通用编程的一种关键思想,允许为模板编写要求,并由编译器检查这些要求。这解决了在实例化模板时可能出现的类型错误问题,使得报错信息更加明确。它改变了我们思考和编写通用代码的方式,通过使用requires关键字,开发者可以在模板参数上设置约束条件,确保模板只在满足特定条件的类型上实例化。
  2. 范围(Ranges) :这个新特性提供了对一系列元素的抽象访问,类似于STL容器,但它更加通用,可以适用于任何类型的数据集,包括数组、向量、列表等。
  3. 协程(Coroutines) :协程是一种编程模型,允许函数在执行过程中挂起并稍后恢复。C++20通过引入新的关键字如co_awaitco_returnco_yield来支持协程,使得异步编程和并发编程更加简单和直观。
  4. 模块(Modules) :模块是C++20中引入的一种新的代码组织方式,它提供了更好的封装和接口定义,有助于减少编译依赖,提高代码的可读性和可维护性。
  5. 三路比较操作符(Three-way Comparison) :C++20引入了<=>操作符,也称为太空船操作符,用于执行三个结果比较:小于、等于或大于。这简化了比较逻辑并提高了代码的可读性。
  6. 常量表达式(constexpr)的更新:C++20对constexpr进行了扩展,允许在更多上下文中使用常量表达式,包括在constexpr函数中使用ifswitch等控制流语句。
  7. 数字分隔符(Digit Separators) :为了提高大数字的可读性,C++20允许在数字字面量中使用单引号作为分隔符。

此外,C++20还包含其他一些次要特性和改进,如改进的智能指针、更好的错误处理等。为了充分利用这些新特性,开发者需要使用支持C++20标准的编译器,并相应地更新编译选项。总的来说,C++20的这些新特性为开发者提供了更强大、更灵活的工具集,有助于提高代码质量、可读性和可维护性,同时简化复杂编程任务的实现。

这些新特性使得C++编程更加现代化、高效和安全。通过利用这些特性,开发者可以编写出更简洁、易读、易维护的代码,并提升程序的性能。

C++的四种强制转换

  1. 静态转换(static_cast):用于将一种数据类型转换为另一种数据类型,但是要求类型之间存在某种相关性,如基类指针转换为派生类指针,或者整型转换为浮点型。
  1. 动态转换(dynamic_cast):用于在多态类层次结构(有虚函数的类)中进行转换,会在运行时检查转换是否安全,如果不安全则返回空指针。
  1. 重新解释转换(reinterpret_cast):用于将一个指针或引用转换为其他类型的指针或引用,没有类型检查,潜在地危险,应该谨慎使用。
  1. 常量转换(const_cast):用于去除指针或引用的常量性,可以在需要修改被声明为常量的变量时使用,但是应该避免滥用。

c++ 内存分布

在C++中,内存分布主要涉及以下几个区域:

  1. 栈区(Stack) :由编译器自动分配和释放,存放函数的参数值、局部变量等。其操作方式类似于数据结构中的栈。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区的内存由系统自动管理,无需程序员手动管理。
  1. 堆区(Heap) :一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。注意它与数据结构中的堆是两回事,分配方式类似于链表。堆内存的管理由程序员负责,使用new关键字分配内存,使用delete关键字释放内存。如果程序员没有正确地管理堆内存,可能会导致内存泄漏等问题。
  1. 全局/静态存储区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。这部分内存的管理也由系统自动完成,程序员无需干预。
  1. 文字常量区:常量字符串就是放在这里的。程序结束后由系统释放。这部分内存也是由系统自动管理的。
  1. 程序代码区:存放函数体的二进制代码。这部分内存是只读的,由操作系统进行管理。

总的来说,C++的内存分布主要涉及栈区、堆区、全局/静态存储区、文字常量区和程序代码区。不同的内存区域有不同的特性和管理方式,程序员需要了解并正确使用这些内存区域,以确保程序的正确性和效率。

C++函数调用的压栈过程

C++函数调用的压栈过程是一个复杂但有序的机制,它确保了函数能够正确地被调用、执行并返回。以下是对这一过程的详细解释:

  1. 保存现场

    • 在函数调用之前,当前的程序计数器(指向下一条将要执行的指令)和栈指针等寄存器的值会被保存在栈中。这是为了函数执行完毕后能够恢复现场,继续执行原来的程序流程。
  2. 压入返回地址

    • 函数的返回地址,即函数调用后将要执行的下一条指令的地址,会被压入栈中。这样,当函数执行完成时,处理器知道应该从哪里继续执行程序。
  3. 传递参数

    • 函数的参数按照从右到左的顺序被压入栈中。这样做是为了在函数内部能够方便地访问这些参数。对于某些调用约定,参数也可能通过寄存器传递,特别是当参数数量较少或类型特殊时。
  1. 压入局部变量空间

    • 当函数被调用时,系统会为该函数的局部变量分配内存空间。这个空间通常位于栈帧中,栈帧是每个函数调用所独有的栈空间部分。局部变量只在其所在的函数内部有效,并在函数退出后被销毁。
  2. 执行函数调用

    • 一旦上述准备工作完成,处理器会跳转到被调用函数的入口点,开始执行函数体中的代码。这包括操作参数、定义和使用局部变量、以及可能的进一步函数调用。
  3. 返回值传递

    • 如果函数有返回值,该值通常会被保存在寄存器中,或者在函数调用前就为其分配好内存空间(如通过指针或引用传递)。对于复杂类型,可能需要按照特定的规则进行拷贝或移动操作。
  4. 恢复现场与返回

    • 当函数执行完毕后,栈上的数据会按照相反的顺序被清理或出栈,以恢复到函数调用前的状态。这包括恢复程序计数器和栈指针等寄存器的值。最后,控制权被返回给调用者,继续执行原来的程序流程。

需要注意的是,具体的压栈和调用过程可能因编译器、操作系统和使用的调用约定而有所不同。上述描述是一个简化和通用的版本,用于帮助理解C++函数调用背后的基本原理。

c++ 源代码编译可执行文件过程

C++从源代码到可执行文件的过程可以归纳为以下几个步骤:

  1. 预处理(Preprocessing)
  • 预处理器读取源代码(通常以.cpp为扩展名),处理其中的预编译指令,如宏定义(#define)、条件编译指令(如#ifdef#ifndef等)、头文件包含指令(#include)等。
  • 预处理后的结果是一个扩展名通常为.i的中间文件,这个文件不包含任何宏定义,因为所有的宏已经被展开,包含的文件也已经被插入到该文件中。

2.编译(Compilation)

  • 编译器将预处理后的文件进行词法分析、语法分析,并生成相应的汇编代码。这个过程中会进行类型检查、语法错误检查等。

  • 编译的结果是一个汇编代码文件,其扩展名通常为.s。在这个阶段,编译器可能还会进行一些优化操作,以提高生成代码的效率。

  1. 汇编(Assembly)
  • 汇编器将编译阶段生成的汇编代码转换成机器语言指令,生成目标文件(Object File),其扩展名通常为.o.obj

  • 每一个汇编语句几乎都对应一条机器指令,汇编器的工作相对简单,主要是进行指令的翻译。

  1. 链接(Linking)
  • 链接器将多个目标文件以及所需的库文件链接成一个可执行文件。这个过程中会解决符号引用问题,例如函数和变量的定义与引用之间的匹配。

  • 链接后的结果就是最终的可执行文件,其扩展名在Windows上通常为.exe,在Linux和macOS上则没有特定的扩展名。

总结来说,C++从源代码到可执行文件的过程包括预处理、编译、汇编和链接四个主要步骤。每个步骤都有其特定的任务和输出文件类型,共同构成了软件开发的编译过程。

volatile关键字

volatile关键字在编程中,特别是在多线程环境下,起着非常重要的作用。以下是关于volatile关键字的详细解释:

  1. 内存可见性
  • 当一个变量被声明为volatile时,它保证了所有线程都能看到该变量的最新值。这意味着,当一个线程修改了这个变量的值,其他线程能够立即看到这个变化。这是通过禁止编译器和处理器对相关代码进行重排序优化来实现的,从而确保多线程环境下内存操作的正确性。
  1. 防止编译器优化
  • volatile关键字告诉编译器不要对标记为volatile的变量的访问进行优化,因为这些变量的值可能会在程序的控制之外被改变。这确保了每次访问都会从内存中读取最新的值,而不是使用可能已经过时的缓存值。
  1. 不保证原子性
  • 尽管volatile关键字确保了内存可见性,但它并不能保证操作的原子性。原子性指的是操作不可分割,即在执行过程中不会被其他线程打断。对于复杂的操作,如自增或自减,volatile无法确保这些操作的原子性,因此可能需要在多线程环境下使用额外的同步机制,如锁。
  1. 使用场景
  • volatile关键字通常用于一些需要确保所有线程都能看到最新值的场景,例如标识位的控制、状态转换等。在Java中,它常被用于实现双重检查锁定模式(Double-Checked Locking Pattern),以确保单例对象的线程安全性。
  1. 与其他同步机制的比较
  • 与锁等更重的同步机制相比,volatile提供了一种更轻量级的线程同步手段。它不会造成线程阻塞,因此在某些性能敏感的场景下可能更为适用。然而,由于它不保证操作的原子性,所以在需要复杂同步逻辑的情况下,可能还需要使用锁或其他同步机制。

综上所述,volatile关键字是一个重要的多线程编程工具,它确保了变量的内存可见性并防止了编译器的某些优化行为。然而,它并不解决所有并发问题,特别是涉及复杂操作原子性的情况。因此,在使用时需要仔细考虑其适用性和限制。

B树、B+树、红黑树和AVL树都是常见的数据结构,它们在计算机科学中有着广泛的应用。下面将分别介绍它们的区别、时间复杂度以及应用场景。

一、区别

  1. B树(B-tree)
  • 是一种自平衡的树,能够保持数据有序。
  • 每个节点可以拥有2个以上的子节点,节点中的关键字数量有一定的范围限制。
  • 所有叶子节点都位于同一层。
  1. B+树
  • 是B树的变体,也是一种多路搜索树。
  • 与B树不同的是,B+树的非叶子节点只保存关键字信息,不保存数据,数据只保存在叶子节点中。
  • 所有的叶子节点通过指针相连,形成一个有序链表,便于范围查询和顺序访问。
  1. 红黑树
  • 是一种自平衡的二叉查找树。
  • 每个节点都有一个颜色属性,可以是红色或黑色。
  • 红黑树满足一系列性质以保持树的平衡,从而确保查找、插入和删除操作的时间复杂度为O(log n)。

二、时间复杂度

  1. B树和B+树:查找、插入和删除操作的时间复杂度通常为O(log n),其中n是树中元素的数量。这是因为B树和B+树都是平衡的多路搜索树,树的高度相对较低。
  2. 红黑树:查找、插入和删除操作的时间复杂度也是O(log n)。红黑树通过颜色和一系列调整规则来保持树的平衡,从而实现高效的查找和更新操作。

三、应用场景

  1. B树和B+树:
  • 数据库索引:B树和B+树常用于数据库系统中作为索引结构,以提高数据检索速度。
  • 文件系统:在文件系统中,B树和B+树被用来组织和管理磁盘上的数据块。
  1. 红黑树:
  • 关联数组:红黑树可以作为关联数组的实现基础,提供高效的键值对存储和查找功能。
  • 内存管理:在内存管理中,红黑树可以用来实现高效的内存分配和回收算法。
  • 其他需要高效查找和更新操作的场景:如网络路由表、编译器符号表等。

总的来说,B树、B+树和红黑树都是高效的数据结构,它们在不同的应用场景中发挥着重要作用。选择哪种数据结构取决于具体的需求和场景特点。

语义转发原理

C++中的语义转发,特别是与“完美转发”相关的概念,是C++11及以后版本中引入的一个重要特性。它主要涉及到模板编程和类型推导,允许函数模板将其接收到的参数(无论是左值还是右值)以原始的形式转发给另一个函数,而不会丢失任何类型信息。这在编写通用代码时特别有用,因为它允许函数模板以完全透明的方式工作,不改变参数的值类别。

以下是关于C++语义转发原理的清晰归纳:

  1. 基本概念
  • 左值与右值:左值是可以取地址的表达式,通常对应内存中的某个具体位置;右值则是不能取地址的表达式,通常表示临时对象或字面值。
  • 引用折叠:是C++中处理模板参数推导时出现双重引用(如T& &)的一种规则。它确保了类型推导的正确性,是完美转发的基础之一。
  1. 关键技术
  • 右值引用T&&):C++11引入的新特性,用于绑定到右值上,从而可以延长右值的生命周期。在完美转发中,右值引用允许我们区分左值参数和右值参数。
  • std::forward:是一个模板函数,用于实现完美转发。它根据给定的模板参数类型(通常是推导出的类型),将参数以原本的形式(左值或右值)转发给另一个函数。
  1. 完美转发的实现
  • 当编写一个接受任意类型参数的函数模板时,我们可以使用“万能引用”(也称为“转发引用”),即template<typename T> void func(T&& arg)的形式。这里的T&&不代表右值引用,而是根据T的推导类型可以是左值引用或右值引用。
  • 在函数体内部,我们使用std::forward<T>(arg)来转发参数。这样做的好处是保留了参数的原始值类别(左值或右值),使得接收转发的函数能够正确处理参数。
  1. 应用场景
  • 完美转发在编写通用包装器、代理函数或工厂函数时非常有用,因为这些函数通常需要将其接收到的参数以不变的形式传递给其他函数或对象。
  • 它还广泛用于实现“移动语义”,即允许资源(如内存)从一个对象转移到另一个对象,从而提高性能并减少不必要的资源复制。
  1. 注意事项
  • 在使用完美转发时,需要确保对模板参数T的推导有深入的理解,以避免意外的类型转换或引用折叠行为。
  • 由于完美转发涉及到复杂的类型操作和模板元编程技巧,因此在编写相关代码时需要格外小心,并充分测试以确保其正确性。

综上所述,C++中的语义转发原理是通过结合右值引用、引用折叠和std::forward等技术实现的,它允许在模板编程中保持参数的原始类型和值类别,从而实现高效且通用的代码编写。

智能指针

在C++中,智能指针是一种资源管理技术,用于自动管理动态分配的内存。C++11引入了三种智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr。这些智能指针可以帮助防止内存泄漏,并确保在不再需要内存时将其释放。

以下是这些智能指针的基本实现和使用方法:

  1. std::unique_ptr

std::unique_ptr是一种独占所有权的智能指针,意味着同一时间只能有一个unique_ptr指向某个对象。当unique_ptr被销毁时(例如超出作用域),它所指向的对象也会被自动删除。

`#include <memory>`
std::unique_ptr<int> ptr(new int(5));`// 使用ptr`
*ptr = 10;`// 当ptr超出作用域时,它所指向的内存会被自动释放`
  1. std::shared_ptr

std::shared_ptr是一种共享所有权的智能指针。多个shared_ptr可以指向同一个对象,并且该对象只有在最后一个指向它的shared_ptr被销毁时才会被删除。

#include <memory>
std::shared_ptr<int> ptr1(new int(5));`
std::shared_ptr<int> ptr2 = ptr1; // ptr1和ptr2现在共享同一个对象的所有权`
// 当ptr1和ptr2都不再存在时,它们所指向的对象会被自动释放
  1. std::weak_ptr

std::weak_ptr是为了配合std::shared_ptr而设计的,它可以从一个shared_ptr或者另一个weak_ptr对象构造,其构造和析构都不会影响引用计数。只有当其升级为shared_ptr时,引用计数才会增加。这种设计是为了解决shared_ptr循环引用的问题。

#include <memory>
std::shared_ptr<int> ptr1(new int(5));
std::weak_ptr<int> ptr2 = ptr1; // ptr2不增加引用计数
// 当需要访问ptr2所指向的对象时,可以将其升级为shared_ptr
if(auto temp_ptr = ptr2.lock()) {
// 使用temp_ptr访问对象
}

请注意,虽然智能指针可以自动管理内存,但仍然需要谨慎使用,以避免出现循环引用等问题。在使用智能指针时,应尽量遵循RAII(Resource Acquisition Is Initialization)原则,即在对象的构造函数中获取资源,在析构函数中释放资源,以确保资源的正确管理。

回调实现

在C++中,回调通常是通过函数指针或者函数对象(如 std::function)来实现的。回调允许一个函数作为参数传递给另一个函数,并在需要的时候被调用。

以下是一个使用函数指针实现回调的简单示例:

#include <iostream>

// 回调函数类型定义
typedef void (*CallbackFunction)(int);
// 一个函数,它接受一个回调函数作为参数
void Process(int data, CallbackFunction callback) 
 {
// 对数据进行一些处理...
// 调用回调函数
callback(data);
}
// 一个简单的回调函数
void MyCallback(int data)
{
std::cout << "Callback called with data: " << data << std::endl;
}

int main()
{
// 调用Process函数,并传入MyCallback作为回调函数
Process(42, MyCallback);
return 0;
}

在这个例子中,我们定义了一个回调函数类型 CallbackFunction,它是一个接受一个整数参数并返回void的函数指针类型。Process 函数接受一个整数和一个回调函数作为参数,在处理完数据后调用回调函数。MyCallback 是一个符合 CallbackFunction 类型的函数,它简单地打印接收到的数据。在 main 函数中,我们调用 Process 函数,并传入 MyCallback 作为回调函数。

另一种实现回调的方法是使用 std::function 和 std::bind(或 lambda 表达式),这种方法更加灵活和类型安全。以下是一个使用 std::function 和 lambda 表达式实现回调的示例:

#include <iostream>
#include <functional>

// 一个函数,它接受一个std::function作为回调函数
void Process(int data, const std::function<void(int)>& callback)
{
// 对数据进行一些处理...
// 调用回调函数
callback(data);
}

int main()
{
// 使用lambda表达式作为回调函数
Process(42, [](int data)
{
std::cout << "Callback called with data: " << data << std::endl;
});
return 0;
}

在这个例子中,我们使用 std::function<void(int)> 来定义回调函数类型,它可以接受任何可调用的对象,包括函数指针、函数对象、lambda 表达式等。在 main 函数中,我们传入一个 lambda 表达式作为回调函数给 Process 函数。

STL容器原理

STL(Standard Template Library)容器是C++标准库提供的一系列模板类,用于存储和管理数据。这些容器具有不同的底层实现、时间复杂度、扩容机制以及应用场景。以下是对这些方面的详细解答:

1. 底层实现

  • vector:底层采用动态数组实现,内存空间连续,支持随机访问。 list:底层采用双向链表实现,内存空间不连续,支持快速增删。 deque:底层采用双向开口的连续线性空间实现,可以看作是list和vector的结合,支持随机访问。 set、multiset、map、multimap:底层采用红黑树(平衡二叉搜索树)实现,元素自动排序。

2. 时间复杂度

  • vector
  • 访问元素:O(1)
  • 插入/删除(尾部):O(1)
  • 插入/删除(中间或头部):O(n)
  • list
  • 访问元素:O(n)
  • 插入/删除:O(1)
  • deque
  • 访问元素:O(1)
  • 插入/删除(头部或尾部):O(1)
  • 插入/删除(中间):O(n)
  • set、multiset、map、multimap
  • 插入/删除/查找:O(log n)

3. 扩容机制

  • vector:当容量不足以存储新元素时,会分配一个更大的内存块(通常是当前容量的两倍),将原有元素复制到新内存中,并释放原来的空间。这种扩容机制可能导致一定的内存浪费和时间开销。
  • list:由于list采用链表结构,不需要扩容。
  • deque:deque采用分段连续空间组合而成,可以动态地增加新的空间段并链接在一起,因此也不需要像vector
  • set、multiset、map、multimap:这些容器基于红黑树实现,红黑树在插入或删除元素时会自动进行平衡调整,因此也不需要扩容操作。

4. 应用场景

  • vector:适用于需要随机访问且频繁在尾部进行操作的场景,如存储大量同类型数据并进行排序、查找等操作。但如果频繁在中间或头部插入/删除元素,则可能导致效率较低。
  • list:适用于需要频繁在任意位置插入/删除元素的场景,如实现双向链表结构的数据管理。但由于不支持随机访问,查找元素可能较慢。
  • deque:适用于需要在头部和尾部频繁插入/删除元素的场景,如实现双端队列结构的数据管理。deque相对于vector在头部操作更加高效。
  • set、multiset、map、multimap:适用于需要根据键值自动排序并快速查找的场景,如实现字典、电话簿等数据结构。这些容器提供了高效的查找、插入和删除操作,并支持根据键值进行排序。