C++ 提高编程:模板与 STL 深度剖析

摘要:本文深入探讨 C++ 提高编程中的模板编程与标准模板库(STL)相关内容。详细阐述模板编程中函数模板和类模板的概念、语法、特性及应用案例;对 STL 的诞生背景、基本概念、六大组件进行剖析,并对常用容器、函数对象、常用算法展开全面讲解,旨在为 C++ 开发者提供系统且深入的技术参考,助力其提升 C++ 编程能力与代码质量。

一、引言

        C++ 作为一种强大且应用广泛的编程语言,其提高编程部分涵盖的模板编程和 STL,为开发者提供了高度的代码复用性和强大的数据处理能力。模板编程实现了泛型编程,使代码能够独立于具体数据类型;STL 则是一套精心设计的模板库,包含容器、算法和迭代器等组件,极大地简化了复杂数据结构和算法的实现。深入理解和掌握这些内容,对提升 C++ 编程水平至关重要。

二、模板编程

(一)模板概念

        模板是 C++ 泛型编程的基础,它允许将类型作为参数传递给函数或类,从而实现代码的复用。通过模板,算法可以独立于具体的数据类型,提高了代码的通用性和可维护性。其核心基于类型参数化,使得同一套代码能够处理不同类型的数据,减少了重复代码的编写。

(二)函数模板

  1. 语法
    函数模板以template <typename T>template <class T>的形式声明模板参数,其中typenameclass含义相同,用于指定类型参数。在函数定义中,使用该模板参数来表示函数的参数类型或返回值类型。例如:
template <typename T>
T add(T a, T b) {return a + b;
}
  1. 注意事项
    类型推导存在一定规则,编译器优先匹配精确类型,当无法自动推导模板参数类型时,需要开发者显式指定。同时,在模板参数匹配时,普通函数的优先级高于函数模板。
  2. 案例
    实现一个通用的数组元素交换函数:
template <typename T>
void swapArray(T arr[], int i, int j) {T temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}

        此函数可用于交换不同类型数组中的元素,展示了函数模板的便捷性。再如编写通用的最大值获取函数:

template <typename T>
T getMax(T a, T b) {return (a > b)? a : b;
}

        体现了函数模板对不同数据类型的适用性。
4. 与普通函数区别
        函数模板具有类型通用性,能够处理多种数据类型;而普通函数针对特定类型编写。在编译时,函数模板会根据实际传入的参数类型进行实例化,生成对应类型的函数代码,普通函数则直接进行编译。
5. 调用规则
        自动类型推导:编译器根据传入函数的参数自动确定模板参数的类型,如int result = add(3, 5);。显式指定类型:开发者明确指定模板参数类型,例如add<int>(3, 5)
6. 局限性
        对于一些特定类型的操作,如位运算等,函数模板可能需要特殊处理。此外,大量实例化可能导致代码体积膨胀,占用更多的存储空间。

(三)类模板

  1. 语法
    类模板的声明形式为template <typename T> class ClassName {...} ,在类内部可以使用模板参数T定义成员变量和成员函数。成员函数既可以在类内直接定义,也可以在类外实现。类外实现成员函数时,语法如下:
template <typename T>
return_type ClassName<T>::function_name(parameters) {...}
  1. 与函数模板区别
    类模板作用于整个类及其对象实例,而函数模板仅作用于函数。类模板可包含多种成员,如数据成员、成员函数等,结构更为复杂,涉及到类的封装、继承和多态等特性与模板的结合。
  2. 成员函数创建时机
    类模板的成员函数在调用时才会进行实例化,生成对应类型的函数代码。提前声明成员函数并不立即实例化,这种延迟实例化机制可以节省编译时间,只有在实际使用时才进行实例化操作。
  3. 对象做函数参数
    按值传递:将对象复制一份传递给函数,这种方式开销较大,适用于小对象或无需保留原对象状态的情况。例如:
template <typename T>
void printObjectByValue(T obj) {std::cout << obj << std::endl;
}

        引用传递:传递对象的引用给函数,高效且能修改对象内容,同时避免了拷贝构造的开销。如:

template <typename T>
void modifyObjectByReference(T& obj) {// 修改obj操作
}

        指针传递:通过指针传递对象,具有较高的灵活性,可实现多态等特性,但需要注意指针的有效性。示例如下:

template <typename T>
void operateOnObjectByPointer(T* ptr) {// 通过指针操作对象
}
  1. 与继承
    类模板继承普通类:子类模板可以继承普通类的属性和方法,并在此基础上添加模板特性。例如,普通类实现了一些通用的功能,类模板继承后可以针对不同类型进行定制化扩展。
    类模板之间继承:子类模板可以继承父类模板的参数及功能,并且可以进一步扩展新的模板参数或成员。在继承过程中,需要注意模板参数的传递和处理,确保继承关系的正确性和代码的可读性。
  2. 成员函数类外实现
    类模板成员函数类外实现时,需遵循特定语法,明确指定模板参数。同时要注意作用域的限定,确保编译器能够正确识别和编译。例如:
template <typename T>
class MyClass {
public:void function();
};template <typename T>
void MyClass<T>::function() {// 函数实现代码
}

  1. 分文件编写
    头文件:主要用于声明类模板及其成员函数,为其他文件提供接口。在头文件中,应包含必要的模板声明和类的前置声明,确保其他源文件能够正确引用。
    源文件:实现类模板的成员函数。在编译时,需要正确处理模板实例化问题,常见的解决方法包括显式实例化或在源文件中包含头文件等方式,以避免编译链接错误。
  2. 与友元
    普通函数友元:授予普通函数访问类模板私有成员的权限,通过在类模板中声明友元函数来实现。例如:
template <typename T>
class MyClass {friend void friendFunction(MyClass<T>& obj);
private:T data;
};template <typename T>
void friendFunction(MyClass<T>& obj) {// 访问obj的私有成员data
}

        函数模板友元:函数模板可以作为类模板的友元,实现特定功能的交互。在声明和实现时,需要注意模板参数的匹配和作用域的处理。
        类模板友元:一个类模板可以作为另一个类模板的友元,从而实现两个类模板之间的数据共享或功能协作。在这种情况下,需要仔细设计友元关系,确保代码的安全性和可维护性。
9. 案例
        实现通用的链表类模板:

template <typename T>
class LinkedList {
private:struct Node {T data;Node* next;Node(const T& value) : data(value), next(nullptr) {}};Node* head;
public:LinkedList() : head(nullptr) {}// 链表操作函数,如插入、删除、遍历等
};

        该类模板可用于管理不同类型数据的链表节点,实现链表的基本操作。再如构建通用的矩阵类模板:

template <typename T>
class Matrix {
private:int rows;int cols;T** elements;
public:Matrix(int r, int c) : rows(r), cols(c) {elements = new T*[rows];for (int i = 0; i < rows; ++i) {elements[i] = new T[cols];}}// 矩阵运算函数,如加法、乘法等
};

        实现矩阵的存储和运算功能,展示类模板在复杂数据结构中的应用。

三、STL 初识

(一)STL 诞生

        STL 起源于惠普实验室,由 Alexander Stepanov 等人开发。其设计目标是提供一套通用、高效、可复用的模板库,旨在简化 C++ 编程中数据结构和算法的实现,减少开发者的重复劳动,提高软件开发的效率和质量。

(二)基本概念

  1. 容器:用于存储数据,是 STL 的重要组成部分。类似于不同的数据结构,如数组、链表、集合等,提供了不同的存储和访问方式,满足各种应用场景对数据存储的需求。
  2. 算法:对容器中的数据进行操作,涵盖排序、查找、遍历等多种操作。STL 算法具有高度的通用性,能够处理不同类型的容器数据,通过迭代器与容器进行交互。
  3. 迭代器:类似于指针,用于访问容器中的元素,为容器提供了统一的访问接口。迭代器的存在使得算法可以不依赖于具体容器的实现细节,实现了容器和算法的分离与通用化。

(三)六大组件

  1. 容器
    顺序容器:如vectorlistdeque等,它们按照元素的插入顺序存储元素。vector是动态数组,具有连续的内存存储,支持随机访问,适合频繁访问元素的场景;list是双向链表,不支持随机访问,但在频繁插入和删除元素时效率较高;deque是双端队列,允许在两端进行高效的插入和删除操作,兼具vectorlist的部分特性。
    关联容器:包括setmap等,这些容器根据特定的键值关系存储元素。set中的元素唯一且自动排序,常用于快速查找和去重;map以键值对的形式存储数据,键唯一且自动排序,可用于高效的键值查找。
  2. 算法
    分为多种类型,如遍历算法用于逐个访问容器中的元素;查找算法用于在容器中寻找特定元素或满足条件的元素;排序算法用于对容器中的元素进行排序等。不同的算法针对不同的应用需求,提供了丰富的数据处理功能。
  3. 迭代器
    输入迭代器:只读,支持单遍扫描容器,用于从容器中读取数据。
    输出迭代器:只写,用于向容器中写入数据。
    前向迭代器:支持读写操作,只能单遍扫描容器,按顺序向前访问元素。
    双向迭代器:支持读写操作,可在容器中前后移动,适用于需要双向遍历的场景。
    随机访问迭代器:支持读写操作,并且可以随机访问容器中的元素,具有类似指针算术运算的能力,如vector的迭代器。
  4. 仿函数
    即函数对象,通过类重载()运算符实现。仿函数可以作为算法的参数,用于定制算法的操作逻辑。例如,在排序算法中,可以使用自定义的仿函数作为比较函数,实现特定的排序规则。
  5. 适配器
    容器适配器:如stackqueue等,它们改变了其他容器的接口,使其符合特定的数据结构特性。stack遵循后进先出原则,queue遵循先进先出原则,它们底层通常基于其他容器(如dequevector等)实现。
    迭代器适配器:如reverse_iterator,它改变了迭代器的遍历方向,使算法可以反向遍历容器。
  6. 分配器
    负责容器的内存分配与释放,默认情况下使用系统的内存分配机制。开发者也可以自定义分配器,以满足特定的内存管理需求,如提高内存分配效率、实现内存池等功能。

(四)容器、算法、迭代器

        迭代器在容器和算法之间起到桥梁作用。算法通过迭代器来访问和操作容器中的元素,使得算法能够独立于具体容器的实现。容器提供了数据的存储结构,而算法基于迭代器对容器中的数据进行处理,这种设计模式实现了容器和算法的分离与高度复用,提高了代码的可维护性和扩展性。

(五)容器算法迭代器初识

  1. vector 存放内置数据类型
    语法:定义vector<int> v;创建一个存储int类型数据的vector容器。插入元素使用v.push_back(5);,访问元素可以通过v[0]v.at(0)at函数带有边界检查)。
    动态内存管理:vector会根据元素的插入自动扩展内存,采用连续内存存储方式,这使得元素访问效率较高,同时也支持随机访问。
  2. vector 存放自定义数据类型
    适配:自定义数据类型需要实现合适的构造函数、拷贝构造函数、赋值运算符等,以满足vector对元素的存储和操作要求。例如,如果自定义类没有正确实现拷贝构造函数,在vector进行元素拷贝(如插入、删除等操作可能涉及拷贝)时可能会出现错误。
    操作:可以对存放自定义数据类型的vector进行排序、查找等操作,但通常需要定制比较函数等,以确保操作符合自定义类型的逻辑。
  3. vector 容器嵌套容器
    应用场景:常用于表示二维数据结构,如二维数组表示矩阵、表格等。例如,vector<vector<int>> matrix;可以表示一个整数矩阵。
    内存布局与访问:外层vector管理内层vector的指针,内存并非完全连续。访问元素时需要通过两层索引,如matrix[i][j]来访问第i行第j列的元素。

四、STL - 常用容器

(一)string 容器

  1. 基本概念
    string容器用于动态存储字符串,它自动管理内存,相比 C 风格字符串更加安全和易用。其内部采用类似vector的结构来存储字符数据,提供了丰富的操作接口来处理字符串。
  2. 构造函数
    默认构造:string s;创建一个空字符串。
    拷贝构造:string s1(s);将已有字符串s复制给s1
    初始化列表构造:string s2("hello");使用字符串字面量初始化s2
  3. 赋值操作
    重载=运算符:s = "world";直接将字符串字面量赋值给s
    assign 函数:提供多种赋值方式,如s.assign("new string", 3);表示取"new string"的前 3 个字符赋值给s
  4. 字符串拼接
    重载+运算符:s = s + "!";"!"拼接到字符串s后面。
    append 函数:s.append(" append");可指定位置和长度进行字符串拼接,如s.append(" append", 3);表示拼接" app"
  5. 查找和替换
    find 函数:size_t pos = s.find("ll");用于查找子串"ll"在字符串s中的位置。
    replace 函数:s.replace(pos, 2, "XX");将从位置pos开始长度为 2 的子串替换为"XX"
  6. 字符串比较
    重载比较运算符:如if (s1 == s2)通过重载的==运算符按字典序比较两个字符串。
    比较规则:从左到右逐个字符比较 ASCII 码值,若所有字符都相同则字符串相等,否则根据第一个不同字符的 ASCII 码大小确定字符串的大小关系。
  7. 字符存取
    重载[]运算符:char c = s[0];直接访问字符串s的第一个字符。
    at 函数:char c1 = s.at(0);[]类似,但at函数带有越界检查,当访问越界时会抛出异常。
  8. 插入和删除
    insert 函数:s.insert(1, "x");在字符串s的索引位置 1 处插入字符"x"
    erase 函数:s.erase(1, 2);删除字符串s从索引位置 1 开始长度为 2 的字符。
  9. 子串
    substr 函数:string sub = s.substr(1, 3);获取字符串s从索引位置 1 开始长度为 3 的子串。
    应用场景:常用于文本解析、字符串截取等操作,例如从一个完整的文件名中截取文件扩展名。

(二)vector 容器

        resize 和 reserve 函数:resize函数用于改变vector中元素的个数,可指定初始值,例如v.resize(10, 0)vector的大小调整为 10,新添加的元素初始值为 0;reserve函数用于调整vector的容量,不改变元素个数,它可以提前分配内存,减少内存重新分配的开销,如v.reserve(20)表示为v预留能容纳 20 个元素的内存空间。
5. 插入和删除
        push_back 函数:在vector的尾部插入元素,v.push_back(6);将元素 6 添加到vector的末尾。
        insert 函数:可以在指定位置插入元素,v.insert(v.begin() + 1, 7);表示在vector的第二个位置(索引为 1)插入元素 7。
        pop_back 函数:删除vector尾部的元素,v.pop_back();移除vector的最后一个元素。
        erase 函数:删除指定位置或范围的元素,v.erase(v.begin() + 2);删除vector中索引为 2 的元素;v.erase(v.begin() + 1, v.begin() + 3);则删除从索引 1 到索引 2(不包括索引 3)的元素。
6. 数据存取
        重载[]运算符:通过v[0]可以直接访问vector中索引为 0 的元素。
        at 函数:v.at(0)[]运算符功能类似,但at函数会进行越界检查,若索引越界会抛出异常,而[]运算符在越界时行为未定义。
7. 互换容器
        swap 函数:swap(v, v1);用于交换两个vector容器的内容。这在某些场景下可以用于内存优化,例如当需要快速交换两个vector的元素时,使用swap函数比逐个元素交换效率更高。
8. 预留空间
        reserve 函数:如前面所述,reserve函数可以提前为vector分配内存,避免在插入元素时频繁进行内存重新分配。例如,当已知需要向vector中插入大量元素时,提前调用reserve函数可以显著提高程序性能。

(三)deque 容器

  1. 基本概念
    deque(双端队列)允许在队列的两端进行插入和删除操作,与vector相比,它在头部和尾部的操作更加高效。其内存结构是分段连续的,通过映射表来管理各个内存块,这使得它在需要频繁在两端操作元素的场景中表现出色。
  2. 构造函数
    默认构造:deque<int> d;创建一个空的deque容器。
    带参数构造:deque<int> d1(5, 8);创建一个包含 5 个元素,每个元素值为 8 的deque
  3. 赋值操作
    重载=运算符:d = d1;d1的内容复制给d
    assign 函数:d.assign(4, 9);用 4 个值为 9 的元素替换d原来的内容。
  4. 大小操作
    size 函数:用于获取deque中元素的个数,int s = d.size();
    empty 函数:判断deque是否为空,bool isEmpty = d.empty();
  5. 插入和删除
    push_front 函数:在deque的头部插入元素,d.push_front(1);将元素 1 添加到deque的开头。
    push_back 函数:在deque的尾部插入元素,d.push_back(2);将元素 2 添加到deque的末尾。
    pop_front 函数:删除deque头部的元素,d.pop_front();移除deque的第一个元素。
    pop_back 函数:删除deque尾部的元素,d.pop_back();移除deque的最后一个元素。
    insert 函数:可以在指定位置插入元素,d.insert(d.begin() + 1, 3);deque的第二个位置(索引为 1)插入元素 3。
    erase 函数:删除指定位置或范围的元素,d.erase(d.begin() + 2);删除deque中索引为 2 的元素;d.erase(d.begin() + 1, d.begin() + 3);删除从索引 1 到索引 2(不包括索引 3)的元素。
  6. 数据存取
    重载[]运算符:通过d[0]可以直接访问deque中索引为 0 的元素。
    at 函数:d.at(0)[]运算符功能类似,但at函数会进行越界检查,若索引越界会抛出异常,而[]运算符在越界时行为未定义。
  7. 排序
    deque可以使用sort算法进行排序,例如sort(d.begin(), d.end());。如果需要按照特定的规则进行排序,可以自定义比较函数,将其作为sort算法的参数,实现对deque中元素的定制化排序。

(四)案例 - 评委打分

  1. 案例描述
    该案例模拟比赛评分的场景,在比赛中多位评委为选手打分,为了保证评分的公正性,需要去除一个最高分和一个最低分,然后计算剩余分数的平均值,以此作为选手的最终得分。此案例涉及到数据的存储、处理和结果的输出等环节,通过使用 STL 容器和算法可以高效地实现。
  2. 实现步骤
    数据录入:使用合适的容器(如vector)来存储评委的分数。例如,vector<double> scores;然后通过循环等方式将评委的分数依次录入到容器中,如scores.push_back(score);(其中score为从输入获取的评委分数)。
    数据处理:首先查找容器中的最高分和最低分,可以使用max_elementmin_element算法,如auto maxIt = max_element(scores.begin(), scores.end());auto minIt = min_element(scores.begin(), scores.end());。然后从容器中删除最高分和最低分,scores.erase(maxIt);scores.erase(minIt);。最后计算剩余分数的平均值,通过累加剩余分数并除以剩余分数的个数来实现,如double sum = accumulate(scores.begin(), scores.end(), 0.0); double average = sum / scores.size();
    结果输出:将计算得到的选手最终得分输出显示,如cout << "选手的最终得分是: " << average << endl;

(五)stack 容器

  1. 基本概念
      stack是一种遵循后进先出(LIFO)原则的数据结构。在实际应用中,常用于函数调用栈的模拟、表达式求值(如后缀表达式计算)等场景。它底层通常基于其他容器(如dequevector)实现,通过对这些容器的接口进行封装,使其符合栈的特性。
  2. 常用接口
    push 函数:用于将元素压入栈顶,s.push(5);将元素 5 添加到栈s的顶部。
    pop 函数:用于移除栈顶的元素,s.pop();删除栈s的栈顶元素。
    top 函数:用于获取栈顶的元素,但不删除该元素,int topVal = s.top();获取栈s的栈顶元素值。
    size 函数:用于获取栈中元素的个数,int stackSize = s.size();
    empty 函数:用于判断栈是否为空,bool isStackEmpty = s.empty();

(六)queue 容器

  1. 基本概念
      queue是一种遵循先进先出(FIFO)原则的数据结构。在实际应用中,常用于消息队列、任务队列等场景,例如在多线程编程中,任务可以按照先进先出的顺序放入任务队列,然后由线程依次取出执行。它底层也通常基于其他容器(如dequelist)实现,通过封装容器接口来满足队列的特性。
  2. 常用接口
    push 函数:用于将元素插入队尾,q.push(3);将元素 3 添加到队列q的末尾。
    pop 函数:用于移除队头的元素,q.pop();删除队列q的队头元素。
    front 函数:用于获取队头的元素,但不删除该元素,int frontVal = q.front();获取队列q的队头元素值。
    back 函数:用于获取队尾的元素,但不删除该元素,int backVal = q.back();获取队列q的队尾元素值。
    size 函数:用于获取队列中元素的个数,int queueSize = q.size();
    empty 函数:用于判断队列是否为空,bool isQueueEmpty = q.empty();

(七)list 容器

  1. 基本概念
      list是一个双向链表,它的每个节点都包含一个指向前驱节点和一个指向后继节点的指针。由于其内存不是连续的,因此不支持随机访问,但是在频繁进行插入和删除操作时具有较高的效率。适用于需要频繁插入和删除元素,而对随机访问需求较少的场景,如实现一个动态的任务列表,任务可以随时添加或删除。
  2. 构造函数
    默认构造:list<int> l;创建一个空的list容器。
    带参数构造:list<int> l1(4, 7);创建一个包含 4 个元素,每个元素值为 7 的list
  3. 赋值操作
    重载=运算符:l = l1;l1的内容复制给l
    assign 函数:l.assign(3, 8);用 3 个值为 8 的元素替换l原来的内容。
  4. 插入和删除
    push_front 函数:在list的头部插入元素,l.push_front(1);将元素 1 添加到list的开头。
    push_back 函数:在list的尾部插入元素,l.push_back(2);将元素 2 添加到list的末尾。
    pop_front 函数:删除list头部的元素,l.pop_front();移除list的第一个元素。
    pop_back 函数:删除list尾部的元素,l.pop_back();移除list的最后一个元素。
    insert 函数:可以在指定位置插入元素,l.insert(l.begin(), 3);list的开头插入元素 3。
    erase 函数:删除指定位置或范围的元素,l.erase(l.begin() + 1);删除list中索引为 1 的元素;l.erase(l.begin() + 1, l.begin() + 3);删除从索引 1 到索引 2(不包括索引 3)的元素。
  5. 数据存取
    由于list不支持随机访问,因此不能像vector那样通过下标直接访问元素。需要通过迭代器来遍历list,例如:
for (auto it = l.begin(); it != l.end(); ++it) {int val = *it;// 对val进行操作
}
  1. 排序
    list可以使用sort函数进行排序,l.sort();。同样,如果需要按照特定的规则进行排序,可以自定义比较函数,将其作为sort函数的参数,实现对list中元素的定制化排序。

(八)set/multiset 容器

  1. 基本概念
    set是一个集合容器,其中的元素唯一且自动排序,底层通常使用红黑树实现。这种数据结构使得在set中查找元素的效率较高,适用于需要快速查找和去重的场景,例如统计一篇文章中不重复的单词。
    multisetset类似,也是基于红黑树实现,但其允许元素重复,适用于需要统计元素出现次数等场景,比如统计一个班级学生成绩中各个分数的出现次数。
  2. 构造和赋值
    默认构造:set<int> s;multiset<int> ms;分别创建一个空的setmultiset容器。
    拷贝构造:set<int> s1(s);multiset<int> ms1(ms);分别将已有的setmultiset容器进行复制。
    赋值运算符重载:s = s1;ms = ms1;分别实现setmultiset容器之间的赋值操作。
  3. 大小和交换
    size 函数:用于获取setmultiset中元素的个数,int setSize = s.size();int multisetSize = ms.size();
    empty 函数:用于判断setmultiset是否为空,bool isSetEmpty = s.empty();bool isMultisetEmpty = ms.empty();
    swap 函数:swap(s, s1);swap(ms, ms1);分别用于交换两个set或两个multiset容器的内容。
  4. 插入和删除
    insert 函数:用于向setmultiset中插入元素,s.insert(5);ms.insert(5);分别向setmultiset中插入元素 5。在set中,如果插入的元素已经存在,则插入操作不会生效;而在multiset中,相同元素可以多次插入。
    erase 函数:用于删除指定的元素或范围,s.erase(s.find(5));ms.erase(ms.find(5));分别删除setmultiset中值为 5 的元素(在multiset中会删除所有值为 5 的元素);s.erase(s.begin(), s.end());ms.erase(ms.begin(), ms.end());分别删除setmultiset中的所有元素。
  5. 查找和统计
    find 函数:用于查找元素,set<int>::iterator it = s.find(5);multiset<int>::iterator mit = ms.find(5);分别在setmultiset中查找值为 5 的元素,并返回指向该元素的迭代器(如果元素不存在,则返回end()迭代器)。
    count 函数:在multiset中用于统计元素出现的次数,int countVal = ms.count(5);统计multiset中值为 5 的元素的个数;在set中,count函数的返回值只能是 0 或 1,因为set中元素唯一。
  6. set 和 multiset 区别
    元素唯一性:set中的元素是唯一的,不允许重复;而multiset允许元素重复出现。
    应用场景:set主要用于去重和快速查找唯一元素;multiset则更适合用于统计元素的出现次数等场景。
  7. pair 对组创建
    语法:可以使用make_pair函数或直接初始化的方式创建pair对组。例如,pair<int, string> p = make_pair(1, "one");pair<int, string> p1(2, "two");pair对组常用于在setmultimap等关联容器中存储键值对。
  8. set 容器排序
    set容器默认按照元素的升序进行排序,这是由其底层的红黑树结构和比较函数决定的。如果需要改变排序规则,可以自定义比较函数。例如:
struct Compare {bool operator()(int a, int b) {return a > b; // 降序排序}
};
set<int, Compare> s2;

        通过这种方式,创建的set容器s2将按照降序对元素进行排序。

(九)map/multimap 容器

  1. 基本概念
    map是一个以键值对(key - value)形式存储数据的关联容器,其中键是唯一的,并且会自动按照键的顺序进行排序,底层通常使用红黑树实现。这种结构使得通过键查找值的效率很高,适用于需要快速查找和存储键值对数据的场景,比如学生成绩管理系统中,以学生学号为键,成绩为值进行存储和查询。
    multimapmap类似,也是存储键值对,但它允许键重复,适用于一个键可以对应多个值的场景,例如一个单词在文章中可能出现多次,需要记录每次出现的位置,就可以使用multimap以单词为键,位置为值进行存储。
  2. 构造和赋值
    默认构造:map<int, string> m;multimap<int, string> mm;分别创建一个空的mapmultimap容器。
    拷贝构造:map<int, string> m1(m);multimap<int, string> mm1(mm);分别将已有的mapmultimap容器进行复制。
    赋值运算符重载:m = m1;mm = mm1;分别实现mapmultimap容器之间的赋值操作。
  3. 大小和交换
    size 函数:用于获取mapmultimap中键值对的个数,int mapSize = m.size();int multimapSize = mm.size();
    empty 函数:用于判断mapmultimap是否为空,bool isMapEmpty = m.empty();bool isMultimapEmpty = mm.empty();
    swap 函数:swap(m, m1);swap(mm, mm1);分别用于交换两个map或两个multimap容器的内容。
  1. 插入和删除
    insert 函数:有多种插入方式。例如,使用m.insert(make_pair(1, "one"));m.insert(std::pair<int, std::string>(1, "one")); 向map容器m中插入键值对。在multimap中插入方式类似,如mm.insert(make_pair(1, "value1"));,由于multimap允许键重复,相同键的键值对会依次插入。
    erase 函数:在map中,m.erase(1);删除键为 1 的键值对;m.erase(m.find(1));通过找到键 1 对应的迭代器来删除键值对。在multimap中,mm.erase(1);会删除所有键为 1 的键值对,mm.erase(mm.find(1));则只删除找到的第一个键为 1 的键值对。
  2. 查找和统计
    find 函数:map<int, string>::iterator it = m.find(1);map m中查找键为 1 的键值对,如果找到,it指向该键值对;否则it等于m.end()。在multimap中类似,multimap<int, string>::iterator mit = mm.find(1);用于查找键为 1 的键值对。
    count 函数:在map中,m.count(1)返回值只能是 0 或 1,因为键唯一。在multimap中,mm.count(1)用于统计键为 1 的键值对出现的次数,若存在多个键为 1 的键值对,会返回相应的数量。
  3. map 容器排序
    map默认根据键的升序进行排序,这基于其底层红黑树结构和默认比较函数。若要改变排序规则,可自定义比较函数。如:
struct KeyCompare {bool operator()(const int& a, const int& b) {return a > b; // 实现键的降序排序}
};
map<int, string, KeyCompare> m2;

        如此创建的map m2会按照键的降序对键值对进行排序。
7. 案例 - 员工分组
案例描述:假设有一个公司,需要将员工按照部门进行分组管理。每个员工有唯一的工号和所属部门信息,要求能够高效地存储员工信息,并能根据部门快速查询该部门的所有员工。此案例体现了mapmultimap在实际场景中的应用。
实现步骤:
定义员工数据结构:

struct Employee {int id;std::string name;std::string department;Employee(int i, const std::string& n, const std::string& d) : id(i), name(n), department(d) {}
};

数据录入:使用multimap来存储员工信息,因为一个部门可能有多个员工。例如:

multimap<std::string, Employee> employeeMap;
Employee emp1(1, "Alice", "HR");
Employee emp2(2, "Bob", "Engineering");
employeeMap.insert(make_pair("HR", emp1));
employeeMap.insert(make_pair("Engineering", emp2));

分组操作:根据部门对员工进行分组。例如,要获取 “HR” 部门的所有员工,可以通过查找键 “HR” 来遍历该部门的所有员工:

auto range = employeeMap.equal_range("HR");
for (auto it = range.first; it != range.second; ++it) {std::cout << "Employee ID: " << it->second.id << ", Name: " << it->second.name << std::endl;
}

结果输出:将分组后的员工信息按照一定格式输出,如上述代码中,将 “HR” 部门的员工工号和姓名输出显示,方便查看和管理。

五、STL - 函数对象

(一)函数对象

  1. 概念
    函数对象是一种可调用对象,通过在类中重载()运算符来实现。它与普通函数的区别在于,函数对象可以携带状态,即它可以包含成员变量来存储额外的信息,而普通函数通常不具备这种能力。函数对象可以像普通函数一样被调用,同时又具有类的特性,能够进行数据封装和继承等操作。
  2. 使用
    在 STL 算法中,函数对象常作为参数传递,用于定制算法的操作逻辑。例如,在sort算法中,可以使用自定义的函数对象作为比较函数,实现特定的排序规则。假设有一个自定义的类MyComparator
class MyComparator {
public:bool operator()(const int& a, const int& b) {return a > b; // 实现降序比较}
};

在使用sort函数时,可以这样调用:

vector<int> numbers = {1, 3, 2, 4, 5};
sort(numbers.begin(), numbers.end(), MyComparator());

这里MyComparator()作为比较函数传递给sort算法,使得numbers向量按照降序排列。

(二)谓词

  1. 概念
    谓词是一种特殊的函数对象,其返回值为bool类型,主要用于条件判断等场景。在 STL 算法中,谓词常用于筛选符合特定条件的元素或进行元素之间的比较。
  2. 一元谓词
    一元谓词是接受一个参数并返回bool值的函数对象。例如,定义一个一元谓词IsEven来判断一个数是否为偶数:
class IsEven {
public:bool operator()(const int& num) {return num % 2 == 0;}
};

在使用find_if算法查找向量中第一个偶数时,可以这样应用:

vector<int> nums = {1, 3, 4, 5, 6};
auto it = find_if(nums.begin(), nums.end(), IsEven());
if (it != nums.end()) {std::cout << "First even number: " << *it << std::endl;
}

  1. 二元谓词
    二元谓词是接受两个参数并返回bool值的函数对象。在排序算法中,常用二元谓词来定义元素之间的比较规则。如前面提到的MyComparator类就是一个二元谓词,它接受两个int类型参数并返回比较结果,用于定义排序顺序。

(三)内建函数对象

  1. 意义
    内建函数对象是 STL 提供的一些预定义的函数对象,它们涵盖了常见的算术、关系和逻辑操作。这些内建函数对象的存在提高了代码编写的效率,开发者无需手动编写这些基本操作的函数对象,直接使用即可。
  2. 算术仿函数
    加法仿函数plusplus<int>()(3, 5)返回3 + 5的结果,即 8。在accumulate算法中结合plus仿函数可用于对容器元素求和,例如:
vector<int> values = {1, 2, 3, 4, 5};
int sum = accumulate(values.begin(), values.end(), 0, plus<int>());

        减法仿函数minusminus<int>()(5, 3)返回5 - 3的结果,即 2。可用于实现两个数的减法操作,在一些自定义算法中可能会用到。
乘法仿函数multipliesmultiplies<int>()(3, 4)返回3 * 4的结果,即 12。在数值计算相关的算法中可能会使用到。
        除法仿函数dividesdivides<int>()(6, 3)返回6 / 3的结果,即 2。用于实现除法运算。
        取模仿函数modulusmodulus<int>()(7, 3)返回7 % 3的结果,即 1。在需要进行取模运算的场景中使用。
3. 关系仿函数
        大于仿函数greatergreater<int>()(5, 3)返回true,因为5 > 3。在sort算法中,如果要对向量进行降序排序,可以使用sort(numbers.begin(), numbers.end(), greater<int>());
小于仿函数lessless<int>()(3, 5)返回true,因为3 < 5。这是sort算法默认的比较方式,用于升序排序。
        大于等于仿函数greater_equalgreater_equal<int>()(5, 5)返回true,因为5 >= 5。在一些需要判断大于等于关系的算法中使用。
小于等于仿函数less_equalless_equal<int>()(3, 3)返回true,因为3 <= 3。常用于条件判断场景。
        等于仿函数equal_toequal_to<int>()(3, 3)返回true,用于判断两个数是否相等。在查找算法中可能会用到,如find_if结合equal_to来查找特定值。
不等于仿函数not_equal_tonot_equal_to<int>()(3, 5)返回true,因为3 != 5。在需要判断不相等关系的场景中使用。
4. 逻辑仿函数
        逻辑与仿函数logical_andlogical_and<bool>()(true, false)返回false,因为true && falsefalse。在一些需要进行逻辑与运算的算法中使用,如在对容器元素进行条件筛选时。
逻辑或仿函数logical_orlogical_or<bool>()(true, false)返回true,因为true || falsetrue。用于逻辑或运算场景,例如多个条件判断中只要有一个条件满足的情况。
逻辑非仿函数logical_notlogical_not<bool>()(true)返回false,因为!truefalse。用于对逻辑值取反的操作。

六、STL - 常用算法

(一)常用遍历算法

  1. for_each
    语法:for_each(iterator start, iterator end, function object)。它接受一个迭代器范围(起始迭代器start和结束迭代器end)以及一个函数对象。
    功能:对容器中指定范围内的每个元素逐一应用传入的函数对象。例如,假设有一个打印整数的函数对象PrintNumber
class PrintNumber {
public:void operator()(const int& num) {std::cout << num << " ";}
};
vector<int> nums = {1, 2, 3, 4, 5};
for_each(nums.begin(), nums.end(), PrintNumber());

上述代码会将nums向量中的每个元素依次打印出来。
应用场景:常用于对容器元素进行简单的遍历操作,如打印元素、对每个元素进行简单的计算等。
2. transform
语法:transform(iterator start, iterator end, output_iterator, function object)transform(iterator1 start, iterator1 end, iterator2 start, output_iterator, function object)。第一种形式将输入范围[start, end)内的元素通过函数对象进行转换,并将结果存储到以output_iterator开始的位置;第二种形式从两个输入范围(分别以iterator1 startiterator2 start开始)中取出元素,通过函数对象进行转换,并将结果存储到output_iterator开始的位置。
功能:对容器元素进行转换并存储到新容器或原容器。例如,将一个向量中的每个元素乘以 2 并存储到另一个向量中:

class MultiplyByTwo {
public:int operator()(const int& num) {return num * 2;}
};
vector<int> source = {1, 2, 3, 4, 5};
vector<int> result(source.size());
transform(source.begin(), source.end(), result.begin(), MultiplyByTwo());

这里MultiplyByTwo函数对象将source向量中的每个元素乘以 2,并将结果存储到result向量中。
应用场景:在数据转换、类型转换等场景中广泛应用,如将字符串向量中的所有字符串转换为大写形式,或者将一个数值向量中的每个元素进行特定的数学运算后存储到新向量。

(二)常用查找算法

  1. find
    语法:find(iterator start, iterator end, value)。它在指定的迭代器范围[start, end)内查找值为value的元素。
    功能:在容器中查找指定值。例如,在一个向量中查找值为 3 的元素:
vector<int> numbers = {1, 2, 3, 4, 5};
auto it = find(numbers.begin(), numbers.end(), 3);
if (it != numbers.end()) {std::cout << "Element 3 found at position: " << std::distance(numbers.begin(), it) << std::endl;
}

如果找到元素,find函数返回指向该元素的迭代器;否则返回end迭代器。
应用场景:常用于判断容器中是否存在特定元素,或者获取特定元素在容器中的位置。
2. find_if
语法:find_if(iterator start, iterator end, predicate)。它在指定的迭代器范围[start, end)内查找满足谓词predicate的第一个元素。
功能:在容器中查找满足条件的元素。例如,查找向量中第一个大于 3 的元素:

class GreaterThanThree {
public:bool operator()(const int& num) {return num > 3;}
};
vector<int> nums = {1, 2, 4, 5, 3};
auto it1 = find_if(nums.begin(), nums.end(), GreaterThanThree());
if (it1 != nums.end()) {std::cout << "First element greater than 3: " << *it1 << std::endl;
}

应用场景:在需要按特定条件筛选元素时使用,如在一个学生成绩向量中查找第一个不及格的成绩。
3. adjacent_find
语法:adjacent_find(iterator start, iterator end)adjacent_find(iterator start, iterator end, binary_predicate)。第一种形式查找相邻且相等的元素;第二种形式使用自定义的二元谓词查找满足特定条件的相邻元素。
功能:查找相邻且相等的元素。例如,在一个向量中查找相邻重复的元素:

vector<int> values = {1, 2, 2, 3, 4};
auto it2 = adjacent_find(values.begin(), values.end());
if (it2 != values.end()) {std::cout << "Adjacent duplicate element found: " << *it2 << std::endl;
}

如果找到相邻重复元素,返回指向第一个重复元素的迭代器;否则返回end迭代器。
应用场景:用于检测序列中是否存在相邻重复的元素,在数据去重、数据校验等场景中可能会用到。
4. binary_search
语法:binary_search(iterator start, iterator end, value)binary_search(iterator start, iterator end, value, compare)。第一种形式在有序的迭代器范围[start, end)内使用默认比较函数二分查找值为value的元素;第二种形式使用自定义的比较函数compare进行二分查找。
功能:在有序容器中二分查找指定值。例如,在一个有序向量中查找值为 4 的元素:

vector<int> sortedNums = {1, 2, 3, 4, 5};
bool found = binary_search(sortedNums.begin(), sortedNums.end(), 4);
if (found) {std::cout << "Element 4 found in the sorted vector." << std::endl;
}

如果找到元素,返回true;否则返回false
应用场景:在需要高效查找有序序列中的元素时使用,相比顺序查找,二分查找的时间复杂度更低,适用于大规模数据的查找。
5. count
语法:count(iterator start, iterator end, value)。它在指定的迭代器范围[start, end)内统计值为value的元素个数。
功能:统计容器中指定值出现的次数。例如,在一个向量中统计值为 2 的元素个数:

vector<int> numbers = {1, 2, 2, 3, 2, 4};
int countVal = count(numbers.begin(), numbers.end(), 2);
std::cout << "The number 2 appears " << countVal << " times." << std::endl;

应用场景:常用于统计元素频率,如统计一篇文章中某个单词出现的次数。
6. count_if
语法:count_if(iterator start, iterator end, predicate)。它在指定的迭代器范围[start, end)内统计满足谓词predicate的元素个数。
功能:统计容器中满足条件的元素个数。例如,统计向量中大于 3 的元素个数:

class GreaterThanThree {
public:bool operator()(const int& num) {return num > 3;}
};
vector<int> nums = {1, 2, 4, 5, 3, 6};
int countGreater = count_if(nums.begin(), nums.end(), GreaterThanThree());
std::cout << "The number of elements greater than 3 is: " << countGreater << std::endl;

应用场景:在需要按条件统计元素数量时使用,如统计一个

班级成绩中及格人数、统计一组数据中奇数的数量等场景。

(三)常用排序算法

  1. sort
    语法:sort(iterator start, iterator end) 或 sort(iterator start, iterator end, comparator)。第一种形式使用默认的小于比较函数(less)对迭代器范围 [start, end) 内的元素进行升序排序;第二种形式则使用自定义的比较函数 comparator 来决定排序规则。
    功能:对容器元素进行排序。例如,对一个存储整数的向量进行升序排序:
std::vector<int> numbers = {5, 3, 7, 1, 9};
std::sort(numbers.begin(), numbers.end());

若要进行降序排序,可自定义比较函数:

struct Greater {bool operator()(const int& a, const int& b) {return a > b;}
};
std::vector<int> numbers = {5, 3, 7, 1, 9};
std::sort(numbers.begin(), numbers.end(), Greater());

应用场景:广泛应用于各种需要对数据进行排序的场景,如对学生成绩进行排名、对商品价格进行排序展示等。
2. random_shuffle
语法:random_shuffle(iterator start, iterator end)。它对指定迭代器范围 [start, end) 内的元素进行随机重排。
功能:随机打乱容器元素顺序。例如,将一副扑克牌(用向量表示)的顺序打乱:

std::vector<int> deck(52);
for (int i = 0; i < 52; ++i) {deck[i] = i;
}
std::random_shuffle(deck.begin(), deck.end());

应用场景:在需要随机化数据顺序的场景中使用,如游戏中的随机发牌、随机分组等。
3. merge
语法:merge(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator) 或 merge(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)。第一种形式将两个有序的输入范围(分别由 iterator1 start 到 iterator1 end 和 iterator2 start 到 iterator2 end 定义)合并到以 output_iterator 开始的输出范围,使用默认的小于比较函数;第二种形式则使用自定义的比较函数 comparator 进行合并。
功能:合并两个有序容器。例如,将两个有序的整数向量合并成一个新的有序向量:

std::vector<int> v1 = {1, 3, 5};
std::vector<int> v2 = {2, 4, 6};
std::vector<int> result(v1.size() + v2.size());
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), result.begin());

应用场景:在数据处理中,当需要将多个有序数据集合并成一个更大的有序数据集时使用,如合并多个有序的日志文件。
4. reverse
语法:reverse(iterator start, iterator end)。它将指定迭代器范围 [start, end) 内的元素顺序反转。
功能:反转容器元素顺序。例如,将一个字符串(用 string 容器表示)反转:

std::string str = "hello";
std::reverse(str.begin(), str.end());

应用场景:在需要改变序列顺序的场景中使用,如将链表反转、将数字的各位顺序反转等。

(四)常用拷贝和替换算法

  1. copy
    语法:copy(iterator start, iterator end, output_iterator)。它将迭代器范围 [start, end) 内的元素拷贝到以 output_iterator 开始的位置。
    功能:将容器元素拷贝到另一个位置。例如,将一个向量的元素拷贝到另一个向量:

std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(source.size());
std::copy(source.begin(), source.end(), destination.begin());

应用场景:常用于数据备份、容器间元素复制等场景,如将一个临时计算结果向量复制到最终存储向量。
2. replace
语法:replace(iterator start, iterator end, old_value, new_value)。它将迭代器范围 [start, end) 内所有值为 old_value 的元素替换为 new_value
功能:将容器中指定值替换为新值。例如,将一个向量中所有的 3 替换为 30:

std::vector<int> numbers = {1, 3, 2, 3, 4};
std::replace(numbers.begin(), numbers.end(), 3, 30);

应用场景:在数据修正场景中使用,如将文本中错误的单词统一替换为正确的单词。
3. replace_if
语法:replace_if(iterator start, iterator end, predicate, new_value)。它将迭代器范围 [start, end) 内所有满足谓词 predicate 的元素替换为 new_value
功能:将容器中满足条件的值替换为新值。例如,将一个向量中所有大于 5 的元素替换为 0:

class GreaterThanFive {
public:bool operator()(const int& num) {return num > 5;}
};
std::vector<int> numbers = {1, 3, 7, 4, 8};
std::replace_if(numbers.begin(), numbers.end(), GreaterThanFive(), 0);

应用场景:在按条件修改数据时使用,如将一组成绩中不及格的分数替换为补考标记。
4. swap
语法:swap(value1, value2)。它交换两个值 value1 和 value2 的内容。
功能:交换两个值。例如,交换两个整数变量的值:

int a = 5;
int b = 3;
std::swap(a, b);

在容器场景中,也可用于交换两个容器的内容,如 std::swap(v1, v2); 交换两个向量 v1 和 v2 的内容。
应用场景:元素交换、容器内容交换等场景,如在排序算法中用于交换元素位置,在需要快速切换两个数据集时交换容器内容。

(五)常用算术生成算法

  1. accumulate
    语法:accumulate(iterator start, iterator end, initial_value) 或 accumulate(iterator start, iterator end, initial_value, binary_function)。第一种形式将迭代器范围 [start, end) 内的元素累加,并加上初始值 initial_value,使用默认的加法操作;第二种形式则使用自定义的二元函数 binary_function 进行累加操作。
    功能:对容器元素进行累加求和。例如,计算一个向量中所有元素的和:

std::vector<int> numbers = {1, 2, 3, 4, 5};
int sum = std::accumulate(numbers.begin(), numbers.end(), 0);

若要进行乘法累加(即计算所有元素的乘积),可自定义二元函数:

struct Multiply {int operator()(const int& a, const int& b) {return a * b;}
};
std::vector<int> numbers = {1, 2, 3, 4, 5};
int product = std::accumulate(numbers.begin(), numbers.end(), 1, Multiply());

应用场景:数值计算场景,如计算一组数据的总和、平均值等,在金融计算、统计分析等领域有广泛应用。
2. fill
语法:fill(iterator start, iterator end, value)。它将迭代器范围 [start, end) 内的所有元素填充为指定值 value
功能:将容器指定范围填充为指定值。例如,将一个向量的所有元素初始化为 0:

std::vector<int> numbers(5);
std::fill(numbers.begin(), numbers.end(), 0);

应用场景:在初始化容器元素、重置容器内容等场景中使用,如在进行多次数据处理前,将结果容器清空并填充为初始值。

(六)常用集合算法

  1. set_intersection
    语法:set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator) 或 set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)。第一种形式求两个有序范围(分别由 iterator1 start 到 iterator1 end 和 iterator2 start 到 iterator2 end 定义)的交集,并将结果存储到以 output_iterator 开始的位置,使用默认的小于比较函数;第二种形式使用自定义的比较函数 comparator
    功能:求两个有序集合的交集。例如,求两个有序向量的交集:

std::vector<int> v1 = {1, 3, 5, 7};
std::vector<int> v2 = {3, 5, 7, 9};
std::vector<int> intersection;
std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(intersection));

应用场景:在需要找出两个数据集的共同部分时使用,如找出两个学生名单中的共同学生、两个商品清单中的重复商品等。
2. set_union
语法:set_union(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator) 或 set_union(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)。第一种形式求两个有序范围的并集,并将结果存储到以 output_iterator 开始的位置,使用默认的小于比较函数;第二种形式使用自定义的比较函数 comparator
功能:求两个有序集合的并集。例如,求两个有序向量的并集:

std::vector<int> v1 = {1, 3, 5};
std::vector<int> v2 = {3, 5, 7};
std::vector<int> unionSet;
std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(unionSet));

应用场景:在合并两个数据集且去除重复元素时使用,如合并两个课程列表并去除重复课程。
3. set_difference
语法:set_difference(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator) 或 set_difference(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)。第一种形式求第一个有序范围相对于第二个有序范围的差集(即存在于第一个范围但不存在于第二个范围的元素),并将结果存储到以 output_iterator 开始的位置,使用默认的小于比较函数;第二种形式使用自定义的比较函数 comparator
功能:求两个有序集合的差集。例如,求向量 v1 相对于向量 v2 的差集:

std::vector<int> v1 = {1, 3, 5, 7};
std::vector<int> v2 = {3, 5};
std::vector<int> difference;
std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(difference));

应用场景:在需要找出两个数据集的差异部分时使用,如找出两个版本软件功能列表的新增功能(相对于旧版本)。

七、总结

  C++ 提高编程中的模板编程和 STL 为开发者提供了强大的工具集。模板编程通过类型参数化实现了代码的高度复用,无论是函数模板还是类模板,都极大地减少了重复代码的编写,提高了代码的通用性和可维护性。STL 作为一套成熟的模板库,其六大组件相互协作,容器提供了多样化的数据存储结构,算法实现了丰富的数据处理操作,迭代器则作为桥梁连接了容器和算法,使得代码能够高效、灵活地处理各种数据场景。通过对常用容器、函数对象和常用算法的深入理解和应用,开发者能够显著提升 C++ 编程的效率和质量,编写出更健壮、更高效的代码。在实际项目开发中,合理运用这些技术能够降低开发成本,提高软件的性能和可扩展性,满足日益复杂的软件需求。因此,深入学习和掌握模板编程与 STL 是 C++ 开发者提升自身能力的关键路径。

  1. copy
    语法:copy(iterator start, iterator end, output_iterator)。它将迭代器范围 [start, end) 内的元素拷贝到以 output_iterator 开始的位置。
    功能:将容器元素拷贝到另一个位置。例如,将一个向量的元素拷贝到另一个向量:
  2. accumulate
    语法:accumulate(iterator start, iterator end, initial_value) 或 accumulate(iterator start, iterator end, initial_value, binary_function)。第一种形式将迭代器范围 [start, end) 内的元素累加,并加上初始值 initial_value,使用默认的加法操作;第二种形式则使用自定义的二元函数 binary_function 进行累加操作。
    功能:对容器元素进行累加求和。例如,计算一个向量中所有元素的和:
  3. set_intersection
    语法:set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator) 或 set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)。第一种形式求两个有序范围(分别由 iterator1 start 到 iterator1 end 和 iterator2 start 到 iterator2 end 定义)的交集,并将结果存储到以 output_iterator 开始的位置,使用默认的小于比较函数;第二种形式使用自定义的比较函数 comparator
    功能:求两个有序集合的交集。例如,求两个有序向量的交集:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/900503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++(类模板的运用)

使用vector实现一个简单的本地注册登录系统 注册&#xff1a;将账号密码存入vector里面&#xff0c;注意防重复判断 登录&#xff1a;判断登录的账号密码是否正确 #include <iostream> #include <vector> #include <fstream> #include <sstream> usi…

【大模型】DeepSeek+蓝耕MaaS平台+海螺AI生成高质量视频实战详解

目录 一、前言 二、蓝耘智能云MaaS平台介绍 2.1 蓝耘智算平台是什么 2.2 平台优势 2.3 平台核心能力 三、海螺AI视频介绍 3.1 海螺AI视频是什么 3.2 海螺AI视频主要功能 3.3 海螺AI视频应用场景 3.4 海螺AI视频核心优势 3.5 项目git地址 四、蓝耘MaaS平台DeepSeek海…

接口自动化学习二:session自动管理cookie

session自动管理cookie&#xff1a; cookie中的数据&#xff0c;都是session提供的 实现步骤&#xff1a; 1.创建session对象&#xff1b;my_sessionrequests.Session() 2.使用session实例&#xff0c;调用get方法&#xff0c;发送获取验证码请求&#xff08;不需要提取cookie&…

C++类型转换详解

目录 一、内置 转 内置 二、内置 转 自定义 三、自定义 转 内置 四、自定义 转 自定义 五、类型转换规范化 1.static_case 2.reinterpret_cast 3.const_cast 4.dynamic_cast 六、RTTI 一、内置 转 内置 C兼容C语言&#xff0c;在内置类型之间转换规则和C语言一样的&am…

QEMU源码全解析 —— 块设备虚拟化(17)

接前一篇文章:QEMU源码全解析 —— 块设备虚拟化(16) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 《KVM实战 —— 原理、进阶与性能调优》—— 任永杰 程舟,机械工业出版社

mac 卸载流氓软件安全助手

之前个人电脑在公司使用过一段时间&#xff0c;为了使用网线联网安装了公司指定的 联软上网助手&#xff0c;谁知安装容易卸载难&#xff0c;后来找运维来卸载&#xff0c;输入管理员密码后&#xff0c;也无反应&#xff0c;最后不了了之了&#xff0c;这个毒瘤软件长期在后台驻…

Java 大视界 -- Java 大数据机器学习模型在智能客服多轮对话系统中的优化策略(179)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

极氪汽车云原生架构落地实践

云原生架构落地实践的背景 随着极氪数字业务的飞速发展&#xff0c;背后的 IT 技术也在不断更新迭代。极氪极为重视客户对服务的体验&#xff0c;并将系统稳定性、业务功能的迭代效率、问题的快速定位和解决视为构建核心竞争力的基石。 为快速响应用户的需求&#xff0c;例如…

Python•判断循环

ʕ⸝⸝⸝˙Ⱉ˙ʔ ♡ 判断🍰常用的判断符号(比较运算符)andor括号notin 和 not inif-elif-else循环🍭计数循环 forrange()函数简易倒计时enumerate()函数zip()函数遍历列表遍历元组遍历字符串遍历字典条件循环 while提前跳转 continue跳出循环 break能量站😚判断🍰 …

FreeRTOS与RT-Thread深度对比:从入门到精通的全面解析

FreeRTOS与RT-Thread深度对比&#xff1a;从入门到精通的全面解析 在嵌入式系统开发领域&#xff0c;实时操作系统(RTOS)扮演着至关重要的角色。FreeRTOS和RT-Thread作为两款主流的开源RTOS&#xff0c;各有特色&#xff0c;适用于不同的应用场景。本文将从小白到大神的角度&a…

OpenCV 图形API(18)用于执行两个矩阵(或数组)的逐元素减法操作函数sub()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 描述 计算两个矩阵之间的逐元素差值。 sub 函数计算两个矩阵之间的差值&#xff0c;要求这两个矩阵具有相同的尺寸和通道数&#xff1a; dst ( I ) src…

LeetCode刷题SQL笔记

系列博客目录 文章目录 系列博客目录1.distinct关键字 去除重复2.char_length()3.group by 与 count()连用4.date类型有个函数datediff()5.mod 函数6.join和left join的区别1. **JOIN&#xff08;内连接&#xff0c;INNER JOIN&#xff09;**示例&#xff1a; 2. **LEFT JOIN&a…

其他合成方式介绍

在 SurfaceFlinger 的 Layer 处理逻辑中&#xff0c;除了常见的 Client Composition&#xff08;GPU合成&#xff09; 和 Device Composition&#xff08;HWC合成&#xff09;&#xff0c;还存在一些特殊的合成方式&#xff0c;比如 Sideband、Solid Color 和 Display Decorati…

GraphRAG与知识图谱

一、GraphRAG介绍 1.1 什么是 Graph RAG&#xff1f; Graph RAG&#xff08;Retrieval-Augmented Generation&#xff09;&#xff0c;是一种基于知识图谱的检索增强技术&#xff0c; 通过构建图模型的知识表达&#xff0c;将实体和关系之间的联系用图的形式进行展示&#xff…

一个开源的 VS Code 大模型聊天插件:Light-at

这篇文章是一个开发杂谈。对于有经验的开发者来说&#xff0c;可能这个项目并不算特别复杂或者高技术&#xff0c;只是对我个人来说算一个里程碑&#xff0c;因此写篇杂谈文章记录一下。也许也能给起步者一些参考。 项目地址&#xff1a;https://github.com/HiMeditator/light-…

SQL121 创建索引

-- 普通索引 CREATE INDEX idx_duration ON examination_info(duration);-- 唯一索引 CREATE UNIQUE INDEX uniq_idx_exam_id ON examination_info(exam_id);-- 全文索引 CREATE FULLTEXT INDEX full_idx_tag ON examination_info(tag);描述 现有一张试卷信息表examination_in…

【Pandas】pandas DataFrame set_flags

Pandas2.2 DataFrame Attributes and underlying data 方法描述DataFrame.index用于获取 DataFrame 的行索引DataFrame.columns用于获取 DataFrame 的列标签DataFrame.dtypes用于获取 DataFrame 中每一列的数据类型DataFrame.info([verbose, buf, max_cols, …])用于提供 Dat…

Linux终止进程(kill process)的一些玩法

经常运行一个程序时&#xff0c;表面上已经终止了&#xff0c;实际上还在后台运行。一来呢&#xff0c;它可能占据端口&#xff0c;导致端口复用的时候报错。二来呢&#xff0c;它可能占用GPU&#xff0c;让你显存直接少一块。 尤其是在多进程程序&#xff0c;假如运行“python…

《比特城传奇:公钥、私钥与网络安全的守护之战》

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 第一章&#xff1a;双钥之谜 比特城的清晨总是笼罩着一层薄雾&#xff0c;仿佛这座城市本身就是由无数个0和1编织而成的幻境。在这里&#xff0c;信息如同空气般无处不在&#xff0c;但…

BGP路由协议之属性1

公认属性是所有 BGP 路由器都必须能够识别的属性 公认必遵 (Well-known Mandatory) : 必须包括在每个 Update 消息里公认任意 (Well-known Discretionary) : 可能包括在某些 Update 消息里。 可选属性不需要都被 BGP 路由器所识别 可选过渡(OptionalTransitive) : BGP 设备不…