主要针对C++泛型编程和STL技术
模板就是建立通用的模具,大大提高代码的复用性
模板特点
函数模板的作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体确定,用一个虚拟的类型来代表
语法
template<typename T>函数声明或定义参数
// 两个整型交换函数void swap(int& a, int& b){ int temp = a; a = b; b = temp;}// 交换浮点型的函数void swap(double& a, double& b){ double temp = a; a = b; b = temp;}// 函数模板template <typename T> // 声明模板,告诉编译器后面代码紧跟着T,不要报错,T是一个通用的数据类型void m_swap(T& a, T& b){ T temp = a; a = b; b = temp;}void test(){ int a = 1; int b = 3; double a1 = 4; double b1 = 5; /* swap(a, b); cout << a << b << endl; swap(a1, b1); cout << a1 << b1 << endl; */ // 使用函数模板 // 1、 自动推导 m_swap(a, b); cout << a << b << endl; // 2、 显示指定类型 m_swap<int>(a, b); cout << a << b << endl;}模板可以将数据类型参数化
模板的使用方法
- 自动推导
- 显示指定类型
注意事项
调用规则如下
如果函数模板和普通函数都可以实现,优先调用普通函数
可以通过空模板参数列表强制调用函数模板
void myPrint(int a, int b){ cout << a << b << endl; cout << "普通函数" << endl;}template<typename T>void myPrint(T a, T b){ cout << a << b << endl; cout << "模板函数" << endl;}void test(){ int a = 10; int b = 20; myPrint<>(a, b); // 空模板参数列表调用模板函数}函数模板也可以发生重载
如果函数模板可以产生更好的匹配模式,优先调用函数模板
void myPrint(int a, int b){ cout << a << b << endl; cout << "普通函数" << endl;}template<typename T>void myPrint(T a, T b){ cout << a << b << endl; cout << "模板函数" << endl;}void test(){ char a = 'a'; char b = 'b'; myPrint(a, b); // 函数模板可以产生更好的匹配 }既然提供了函数模板,最好不要提供普通函数,否则容易出现二义性
如果传入的是一个元组以及自定义数据类型,就无法实现了
因此,C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化模板
// 模板重载// 对比两个数据是否相等class Person{public: Person(string name, int age) { m_Age = age; m_Name = name; } string m_Name; int m_Age;};template<class T>bool myCompare(T& a, T& b) // 如果传入的是一个自定义数据类型呢{ if (a == b) { return true; } else { return false; }}// 利用具体化Person的版本实现代码,具体化优先调用// 也可以使用运算符重载template<>bool myCompare(Person& p1, Person& p2){ if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age) { return true; } else { return false; }}void test(){ Person p1("Tom", 10); Person p2("Tom", 10); cout << myCompare(p1, p2) << endl;}学习模板并不是为了写模板,而是在STL中能够运用系统提供的模板
类模板作用
语法
template<typename T>类参数
template<typename NameT, typename AgeT>class Person{public: Person(NameT name, AgeT age) { m_Age = age; m_Name = name; } NameT m_Name; AgeT m_Age;};void test(){ Person<string, int>("Tom", 30); // 调用-只有一种调用方式}类模板与函数模板区别主要有两点
类模板没有自动类型推导的使用方式
类模板在模板参数列表中可以有默认参数
template<typename NameT, typename AgeT = int> // 默认参数class Person{public: Person(NameT name, AgeT age) { m_Age = age; m_Name = name; } NameT m_Name; AgeT m_Age;};void test(){ Person<string>("Tom", 30);}类模板中成员函数和普通类中成员函数创建时机是有区别的
class Person1{public: void show() { cout << "Person1" << endl; }};template<typename T>class Person{public: // 没调用,其不会编译,因为无法确定T的数据类型 T p1; void func1() { p1.show(); } };void test(){ Person<Person1> p; p.func1();}类模板实例出的对象,向函数传参
一共有三种传入方式
指定传入的数据类型:直接显示对象的数据类型
// 指定传入类型void printPerson1(Person<string, int> &p); 参数模板化:将对象中的参数变为模板进行传递
// 参数模板化template<class T1, class T2>void printPerson2(Person<T1, T2>& p);整个类模板化:将这个对象类型模板化进行传递
// 整个类模板化template<class T>void printPerson3(T &p);// 类模板做函数的参数template<class T1, class T2>class Person{public: Person(T1 name, T2 age) { m_Name = name; m_Age = age; } T1 m_Name; T2 m_Age; void showPerson() { cout << "name:" << m_Name << " age:" << m_Age << endl; }};// 指定传入类型void printPerson1(Person<string, int> &p) { p.showPerson();}// 参数模板化template<class T1, class T2>void printPerson2(Person<T1, T2>& p){ p.showPerson(); cout << "T1的类型为:" << typeid(T1).name() << endl; cout << "T2的类型为:" << typeid(T2).name() << endl;}// 整个类模板化template<class T>void printPerson3(T &p){ p.showPerson();}void test(){ Person<string, int> p("Tom", 12); printPerson1(p); printPerson2(p); printPerson3(p);}查看数据类型的方式
typeid(T2).name()
当类模板碰到继承是,需要注意以下几点
// 类模板与继承template<class T>class Base{public: T m_M;};// 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中 T 的数据类型class Son : public Base<string> {};// 如果想灵活使用父类中的 T 类型,子类也需要变为类模板template<class T1, class T2>class Son1 : public Base<T2> {};能够掌握类模板中的成员函数类外实现
// 类外实现template<class T1, class T2>class Person{public: Person(T1 age, T2 name); void shouPerson(); T1 m_Age; T2 m_Name;};template <class T1, class T2>Person<T1, T2>::Person(T1 age, T2 name){ this->m_Name = name; m_Age = age;}// 要体现其为类模板的类函数,没有参数也要添加template <class T1, class T2>void Person<T1, T2>::shouPerson(){ cout << "name:" << m_Name << " age:" << m_Age << endl;}掌握类模板成员函数分文件编写产生的问题以及解决方式
问题
解决
person.hpp 中代码
#pragma once#include <iostream>using namespace std;template<class T1, class T2>class Person{public: Person(T1 age, T2 name); void shouPerson(); T1 m_Age; T2 m_Name;};template <class T1, class T2>Person<T1, T2>::Person(T1 age, T2 name){ this->m_Name = name; m_Age = age;}template <class T1, class T2>void Person<T1, T2>::shouPerson(){ cout << "name:" << m_Name << " age:" << m_Age << endl;}程序入口代码
#include <iostream>using namespace std;#include <fstream>// 第一中解决方式,包含 .cpp源文件// #include"Person.cpp"// 第二种解决方式,将 .h 和 .cpp 中的内容写到 .hpp 文件中#include "Person.hpp"void test(){ Person<string, int> p("Tom", 132); p.shouPerson();}int main() { test(); system("pause"); return 0;}主要解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为 .hpp
掌握类模板配合友元函数的类内和类外实现
// 通过全局函数打印全局信息 // 提前让编译器知道Person类的存在template<class T1, class T2>class Person;// 如果是类外实现的话需要让编译器提前知道该函数存在template<class T1, class T2>void printPerson1(Person<T1, T2>& p);template<class T1, class T2>class Person{ // 全局函数,类内实现 friend void printPerson(Person<T1, T2>& p) { cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl; } // 全局函数,类外实现 friend void printPerson1<>(Person<T1, T2>& p); // <> 其为函数模板声明public: Person(T1 name, T2 age) { m_Name = name; m_Age = age; }private: T1 m_Name; T2 m_Age;};template<class T1, class T2>void printPerson1(Person<T1, T2>& p){ cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl; cout << "类外实现" << endl;}建议全局函数做类内实现,用法简单,而且编译器可以直接识别
案例描述:实现一个通用的数组类,要求如下
opertator=防止出现浅拷贝的问题myArray.hpp 中代码
#pragma once#include<iostream>using namespace std;template<class T1> // 输出函数void printArray();template<class T1>class MyArray{public: MyArray(int capacity); // 有参构造 MyArray(const MyArray& arr); // 拷贝构造 MyArray& operator=(const MyArray& arr); // 赋值运算符重载,防止浅拷贝问题 void pushBack(const T1& val); // 尾插法插入数据 void delBack(); // 尾删法删除数据 T1& operator[](int index); // 重载[],使得可以使用索引访问数组,同时可以赋值 int getCapacity();// 返回数组的容量 int getSize();// 返回数组的大小 ~MyArray(); // 清空堆区数据private: T1* pAddress; // 指针指向开辟到堆区的真实数组 int m_Capacity; // 数组容量 int m_Size; // 数组大小};template<class T1>MyArray<T1>::MyArray(int capacity){ this->m_Capacity = capacity; this->m_Size = 0; this->pAddress = new T1[this->m_Capacity]; // 开辟数组空间}template<class T1>MyArray<T1>::~MyArray(){ if (this->pAddress) { delete[] pAddress; pAddress = NULL; }}template<class T1>MyArray<T1>::MyArray(const MyArray& arr){ this->m_Capacity = arr.m_Capacity; this->m_Size = arr.m_Size; // this->pAddress = arr.pAddress // 浅拷贝 this->pAddress = new T1[arr.m_Capacity]; // 深拷贝 // arr中的数据都拷贝过去 for (int i = 0; i < this->m_Size; i++) { this->pAddress[i] = arr.pAddress[i]; }}template<class T1>MyArray<T1>& MyArray<T1>::operator=(const MyArray& arr){ // 先判断原来堆区是否有数据,如果有先释放 if (this->pAddress) { delete[] pAddress; this->pAddress = NULL; this->m_Size = 0; this->m_Capacity = 0; } // 深拷贝 this->m_Capacity = arr.m_Capacity; this->m_Size = arr.m_Size; this->pAddress = new T1[this->m_Capacity]; for (int i = 0; i < this->m_Size; i++) { this->pAddress[i] = arr.pAddress[i]; } return *this;}template<class T1>void MyArray<T1>::pushBack(const T1& val){ // 判断容量是否等于大小 if (this->m_Capacity == this->m_Size) { cout << "达到数组容量,无法插入" << endl; return; } this->pAddress[this->m_Size] = val; this->m_Size++; // 更新数组大小}template<class T1>void MyArray<T1>::delBack(){ // 让用户访问不到最后一个元素,即为尾删,逻辑删除 if (!this->m_Size) { cout << "数组中没有元素" << endl; return; } this->m_Size--; // 访问不到那个元素}template<class T1>T1& MyArray<T1>::operator[](int index){ return this->pAddress[index];}template<class T1>int MyArray<T1>::getCapacity(){ return this->m_Capacity;}template<class T1>int MyArray<T1>::getSize(){ return this->m_Size;}template<class T1>void printArray(MyArray<T1>& arr){ for (int i = 0; i < arr.getSize(); i++) { cout << arr[i] << endl; }}主函数调用
#include <iostream>using namespace std;#include <fstream>#include "Person.hpp"void test(){ MyArray<int> arr(5); for (int i = 0; i < 5; i++) { arr.pushBack(i); } cout << "开始输出数组" << endl; printArray(arr);}int main() { test(); system("pause"); return 0;}该数组也可以存储自定义数据类型
STL 大体分为六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
容器:置物之所也
STL 容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组、列表、树、栈、队列、集合、映射表等
这些容器分为序列式容器和关联式容器两种
算法:问题之解也
有限的步骤,解决逻辑或数学上的问题,这叫做算法
算法分为:质变算法和非质变算法
迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含有的各个元素,而又无需暴露该容器的内部表示方式
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针
迭代器种类
| 种类 | 权限 | 支持运算 |
|---|---|---|
| Input iterator(输入迭代器) | 只读 | ++、==、!= |
| Output iterator(输出迭代器) | 只写 | ++ |
| Forward iterator(前向迭代器) | 读和写,并且推进迭代器 | ++、==、!= |
| Bidirectional iterator(双向迭代器) | 读和写,可以向前或向后操作 | ++、-- |
| Random access iterator(随机访问迭代器) | 读和写。可以跳跃式访问任意数据 | ++、--、[n]、-n、<、> |
常用的容器中迭代器种类为双向迭代器和随机访问迭代器
容器:vector
算法:for_each
迭代器:vector<int>::iterator
#include <vector> // vector 头文件#include <algorithm> // 标准算法头文件void printVector(int value){ cout << value << endl;}// vector 存放内置数据类型void test(){ // 创建一个 vector 容器——数组 vector<int> v; // 向容器中插入数据 v.push_back(10); // 尾插数据 v.push_back(11); v.push_back(12); // 通过迭代器访问容器中的数据 vector<int>::iterator itBegin = v.begin(); // 起始迭代器,指向容器中第一个元素,当做指针使用 vector<int>::iterator itEnd = v.end(); // 结束迭代器,指向容器最后一个元素的下一个位置 // 第一种遍历方式 while (itBegin != itEnd) { cout << *itBegin << endl; itBegin++; } // 第二种遍历方式 for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << *it << endl; } // 第三种遍历方式 for_each(v.begin(), v.end(), printVector); // 回调函数}vector 中存放自定义数据类型,并打印输出
// 存放自定义数据类型class Person{public: Person(string name, int age) { m_Name = name; m_Age = age; } int m_Age; string m_Name;};void test(){ vector<Person> v; Person p1("a", 20); Person p2("b", 34); Person p3("c", 20); v.push_back(p1); v.push_back(p2); v.push_back(p3); // 遍历数据 for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) { cout << "name:" << it->m_Name << " age:" << it->m_Age << endl; // it是一个指针 } // 存放自定义数据类型的指针 vector<Person*> v1; v1.push_back(&p1); v1.push_back(&p2); v1.push_back(&p3); // 遍历数据 for (vector<Person*>::iterator its = v1.begin(); its != v1.end(); its++) { cout << "name:" << (*its)->m_Name << " age:" << (*its)->m_Age << endl; // it是一个指针 }}容器中嵌套容器,我们将所有数据遍历输出
// 容器嵌套容器void test(){ vector<vector<int>> V; // 创建小容器 vector<int> v1; vector<int> v2; vector<int> v3; vector<int> v4; // 向小容器中添加数据 for (int i = 0; i < 10; i++) { v1.push_back(i); v2.push_back(10 - i); v3.push_back(20 - i); v4.push_back(i + 10); } // 将小容器插入到大容器中 V.push_back(v1); V.push_back(v2); V.push_back(v3); V.push_back(v4); // 遍历大容器 for (vector<vector<int>>::iterator i = V.begin(); i != V.end(); i++) { // *i 是一个容器 for (vector<int>::iterator j = (*i).begin(); j != (*i).end(); j++) { cout << *j << "\t"; } cout << endl; }}每个容器都要添加头文件
本质
string 和 char* 的区别
特点
构造函数原型
string(); 创建一个空字符串
string(const char* s); 使用字符串 s 初始化
string(const string& str); 使用一个 string 对象初始化另一个 string 对象
string(int, char c); 使用 n 个字符 c 初始化
/*- string(); 创建一个空字符串 string(const char* s); 使用字符串 s 初始化- string(const string& str); 使用一个 string 对象初始化另一个 string 对象- string(int, char c); 使用 n 个字符 c 初始化*/void test(){ string s1; // 默认构造 const char* str = "hello world"; string s2(str); // 有参构造 cout << "s2:" << s2 << endl; string s3(s2); // 拷贝构造 cout << "s3:" << s3 << endl; string s4(10, 'a'); // 10 个 a 构造 cout << "s4:" << s4 << endl;}string 的多种构造方式没有可比性,灵活性较高
功能描述
赋值函数原型
string& operator=(const char* s); // char* 类型字符串赋值给当前的字符串string& operator=(const string &s); // 把字符串 s 赋值给当前的字符串string& operator=(char c); // 把字符赋值给当前的字符串string& assign(const char* s); // 把字符串 s 赋值给当前的字符串string& assign(const char* s, int n); // 把字符串 s 的前 n 个字符赋值给当前的字符串string& assign(const string &s); // 把字符串 s 赋值给当前的字符串string& assign(int n, char c); // 把 n 个字符 c 赋值给当前字符串功能描述
函数原型
string& operator+=(const char* str); // 重载 += 操作符string& operator+=(const char c); // 重载 += 操作符string& operator+=(const string& str); // 重载 += 操作符string& append(const char* s); // 把字符串 s 连接到当前字符串末尾string& append(const char* s, int n); // 把字符串 s 的前 n 个字符连接到字符串结尾string& append(const string& s); // 同 operator+=(const string& str);string& append(const string& s, int pos, int n); // 字符串 s 从 pos 开始的 n 个字符连接到字符串结尾功能描述
函数原型
int find(const string& str, int pos = 0) const; // 查找 str 第一次出现的位置,从 pos 开始查找int find(const char* s, int pos = 0) const; // 查找 s 第一次出现的位置,从 pos 开始查找int find(const char* s, int pos, int n) const; // 从 pos 位置查找 s 的前 n 个字符第一次出现的位置int find(const char c, int pos = 0) const; // 查找字符 c 第一次出现的位置int rfind(const string& str, int pos = npos) const; // 查找 str 最后一次位置,从 pos 开始查找int rfind(const char* s, int pos = npos) const; // 查找 s 最后一次出现的位置,从 pos 开始查找int rfind(const char* s, int pos, int n) const; // 从 pos 查找 s 的前 n 个字符最后一次出现的位置int rfind(const char c, int pos = 0) const; // 查找字符 c 最后一次出现的位置string& replace(int pos, int n, const string& str); // 替换从 pos 开始 n 个字符为字符串 strstring& replace(int pos, int n, const char* s); // 替换从 pos 开始的 n 个字符为字符串 s总结
- find查找是从左往右,rfind是从右往左
- find找到字符串后返回查找的第一个字符位置,找不到返回-1
- replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
功能描述
比较方式
函数原型
int compare(const string& s) const; // 与字符串 s 进行比较int compare(const char* s) const; // 与字符串 s 进行比较主要比较两个字符串是否相等
string 中单个字符存取方式有两种
char& operator[](int n); // 通过 [] 方法获取字符char& at(int n); // 通过 at 方法获取字符str.size(); // 返回字符串的长度 可以修改字符,
str[int n] = 'c'
功能描述
函数原型
string& insert(int pos, const char* s); // 插入字符串string& insert(int pos, const string& str); // 插入字符串string& insert(int pos, int n, char c); // 在指定位置插入 n 个字符 cstring& erase(int pos, int n = npos); // 删除从 pos 开始的 n 个字符功能描述
函数原理
string substr(int pos = 0, int n = npos) const; // 返回由 pos 开始的 n 个字符组成的字符串功能
vector 与普通数组的区别
不同之处在于数组是静态空间,而 vector 可以动态扩展
动态扩展
并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝到新空间,释放原空间

vector 容器的迭代是支持随机访问的迭代器
功能描述
函数原型
vector<T> v; // 采用模板实现类实现,默认构造函数vector v2(v.begin(), v.end()); // 将 v[begin(), end()] 区间中的元素拷贝到自身,左闭右开vector v3(n, elem); // 构造函数将 n 个 elem 拷贝给本身vector v4(const vector& vec); // 拷贝构造函数vector 的多种构造方式没有可比性,灵活使用即可
功能描述
函数原理
vector& operator=(const vector &vec); // 重载赋值操作符assign(v.begin(), v.end()); // 将v[begin, end]区间中的数据拷贝赋值给本身assign(n, elem); // 将 n 个 elem 拷贝赋值给本身功能描述
函数原型
empty(); // 判断容器是否为空capacity(); // 容器的容量size(); // 返回容器中元素的个数resize(int num); // 重新指定容器的长度为 num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除(默认为0)resize(int num, elem); // 重新指定容器的长度为 num,若容器边长,则以 elem 值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除void printVector(vector<int>& v){ for (vector<int>::iterator i = v.begin(); i != v.end(); i++) { cout << *i << " "; } cout << endl;}void test(){ vector<int> v; for (int i = 0; i < 10; i++) { v.push_back(i); } cout << v.capacity() << endl; cout << v.size() << endl; v.resize(20); // 默认使用0填充 cout << v.size() << endl; cout << v.capacity() << endl; printVector(v);}容量大于等于大小
功能描述
函数原型
push_back(elem); // 尾部插入元素elempop_back(); // 删除最后一个元素insert(const_iterator pos, elem); // 迭代器指向位置 pos 插入元素 eleminsert(const_iterator pos, int n, elem); // 迭代器指向位置 pos 插入 n 个元素erase(const_iterator pos); // 删除迭代器指向的长度erase(const_iterator start, const_iterator end); // 删除迭代器从 start 到 end 之间的元素,左闭右开clear(); // 删除容器中所有元素?
v1.insert(v1.begin(), 100); // 第一个参数是迭代器
功能描述
数据原型
at(int idx); // 返回索引 idx 所指的对象operator[](int idx); // 返回索引 idx 所指的数据fornt(); // 返回容器中第一个数据元素back(); // 返回容器中最后一个数据元素功能描述
函数原型
swap(v); // 将 vec 与 本身的元素互换void test(){ vector<int> v; vector<int> v1; for (int i = 0; i < 10; i++) { v.insert(v.begin(), i); v1.push_back(i + 100); } cout << "交换前" << endl; printVector(v); printVector(v1); v.swap(v1); cout << "交换后" << endl; printVector(v); printVector(v1);}作用:巧用 swap 可以收缩内存空间
vector<int> (v).swap(v); // 使用匿名对象
功能描述
函数原理
reserve(int len); // 容器预留 len 个长度,预留位置不初始化,元素不可访问void test(){ vector<int> v; int num = 0; // 统计开辟次数 v.reserve(1000000); // 当没有添加此代码时,开辟了 35 次内存空间 int* p = NULL; for (int i = 0; i < 1000000; i++) { v.push_back(i); if (p != &v[0]) // 开辟一次内存,其首地址会发生改变 { p = &v[0]; num++; } } cout << num << endl;}如果数据量比较大,可以一开始利用 reserve 预留空间
功能
deque 和 vector 区别

deque 内部工作原理
deque 内部有一个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用 deque 时像一片连续的内存空间

deque 容器的迭代器也是支持随机访问的
功能描述
函数原理
deque<T> deq; // 默认构造形式deque d2(deq.begin(), deq.end()); // 构造函数将 [beg, end] 区间中的元素拷贝给本身deque d3(n, elem); // 构造函数将 n 个 elem 拷贝给本身deque d4(const deque &deq); // 拷贝构造函数功能描述
函数原理
deque& operator=(const deque& d); // 重载赋值运算符assign(beg, end); // 将 [beg, end] 区间中的数据拷贝赋值给本身assign(n, elem); // 将 n 个 elem 拷贝赋值给本身功能描述
函数原理
empty(); // 判断容器是否为空size(); // 返回容器中元素的个数resize(int num); // 重新指定容器的长度为 num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除(默认为0)resize(int num, elem); // 重新指定容器的长度为 num,若容器边长,则以 elem 值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除deque 没有容量的概念
功能描述
函数原型
// 两端操作push_back(elem); // 在容器尾部添加一个数据push_front(elem); // 在容器头部插入一个数据pop_back(); // 删除容器最后一个数据pop_front(); // 删除容器第一个元素// 指定位置操作insert(pos, elem); // 在 pos 位置插入一个 elem 元素的拷贝,返回数据的位置insert(pos, n, elem); // 在 pos 位置插入 n 个 elem 数据,无返回值insert(pos, beg, end); // 在 pos 位置插入 (d1.begin(), d1.end()) 数据,无返回值clear(); // 清空容器的所有数据erase(beg, end); // 删除 [beg, end] 区间的数据,返回下一个数据的位置erase(pos); // 删除 pos 位置的数据,返回下一个数据的位置里面的 pos 是迭代器指针的位置
功能描述
函数原型
at(int idx); // 返回索引 idx 所指的数据operator[])(int idx); // 返回索引 idx 所指的值front(); // 返回容器第一个数据元素back(); // 返回容器最后一个数据元素功能描述
函数原型
sort(iterator beg, iterator end); // 对 [beg, end] 区间内元素进行排序注意使用时,要包含头文件
#include <algorithm>对于支持随机访问的迭代器的容器,都可以利用 sort 算法直接对其进行排序
vector 也可以利用 sort 进行排序
有五名选手:选手 ABCDE ,10个评委分别对每一名选手打分,去除最高分,去除评委中最低分,取平均分
// 选手类class Person{public: Person(string name, int score) { m_Name = name; m_Score = score; } string m_Name; int m_Score; // 平均分};void createPerson(vector<Person>& v){ // 创建五名选手 for (int i = 0; i < 5; i++) { char nameSeed[] = { 'A', 'B', 'C', 'D', 'E' }; string name = "选手"; name += nameSeed[i]; int score = 0; // 默认为0分 Person p(name, score); v.push_back(p); }}void setScore(vector<Person>& v){ // 打分 for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) { // 将评委的分数放入deque容器中 deque<int> d; for (int i = 0; i < 10; i++) { int score = rand() % 41 + 60; // 分数在 60 到 100之间,随机分 d.push_back(score); // 将分数放入容器中 } // 排序 sort(d.begin(), d.end()); // 去除最高分,和最低分 d.pop_front(); d.pop_back(); // 取平均分 int sum = 0; for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++) { sum += *dit; // 累加分数 } int avr = sum / d.size(); it->m_Score = avr; }}void showScore(vector<Person> v){ for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) { cout << "名字为:" << it->m_Name << " 平均分为:" << it->m_Score << endl; }}void test(){ // 随机数种子 srand((unsigned int)time(NULL)); vector<Person> v; // 存放选手类 v.reserve(5); createPerson(v); setScore(v); showScore(v);}概念:stack 是一种先进后出的数据结构,它只有一个出口

栈中进入元素称为入栈:push();
栈中弹出元素称为出栈:pop();
功能描述:
stack<T> stk; // stack 采用模板实现,stack对象的默认构造形式stack stk1(const stack& stk); // 拷贝构造函数stack& operator=(const stack& stk); // 重载赋值运算符empty(); // 判断堆栈是否为空size(); // 返回栈的大小push(elem); // 向栈顶添加元素pop(); // 从栈顶移除第一个元素top(); // 返回栈顶元素概念:

队列容器允许从一端新增元素,从另一端移除元素
队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
队列中进数据称为入队:push();
队列中出数据称为出队:pop();
功能描述
queue<T> q; // queue 采用模板类实现,queue 对象的默认构造函数queue(const queue& que); // 拷贝构造函数queue& operator=(const queue& q); // 重载赋值操作符empty(); // 判断堆栈是否为空size(); // 返回栈大小push(elem); // 往队尾添加元素pop(); // 从队头移除第一个元素back(); // 返回最后一个元素front(); // 返回队头第一个元素功能:将数据进行链式存储
链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表是由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL 中的链表是一个双向循环链表

由于链表的存储方式并不是连续的内存空间,因此链表 list 中的迭代器只支持前移或后移,属于双向迭代器
list 优点
list 缺点
list 有一个重要的性质,插入操作和删除操作都不会造成原有 list 迭代器的失效,这在 vector 是不成立的
总结:STL 中 list 和 vector 是最常被使用的容器,各有优缺点
功能描述
函数原型
list<T> l; // list 采用模板类实现,对象的默认构造形式list(beg, end); // 构造函数将 [beg, end]区间中的元素拷贝给本身list(n, elem); // 构造函数将 n 个 elem 拷贝给本身list(const list& l); // 拷贝构造函数功能描述
函数原型
assign(beg, end); // 将 [beg, end] 区间中的数据拷贝赋值给本身assign(n, elem); // 将 n 个 elem 拷贝赋值给本身list& operator=(const list& l); // 重载赋值运算符swap(l); // 将 list 与本身的元素互换功能描述
函数原型
size(); // 返回容器中元素的个数empty(); // 判断容器是否为空resize(int num); // 重新指定容器的长度为 num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除(默认为0)resize(int num, elem); // 重新指定容器的长度为 num,若容器边长,则以 elem 值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除功能描述
函数原型
push_back(elem); // 在容器尾部添加一个数据push_front(elem); // 在容器头部插入一个数据pop_back(); // 删除容器最后一个数据pop_front(); // 删除容器第一个元素insert(pos, elem); // 在 pos 位置插入一个 elem 元素的拷贝,返回数据的位置insert(pos, n, elem); // 在 pos 位置插入 n 个 elem 数据,无返回值insert(pos, beg, end); // 在 pos 位置插入 (l.begin(), l.end()) 数据,无返回值clear(); // 清空容器的所有数据erase(beg, end); // 删除 [beg, end] 区间的数据,返回下一个数据的位置erase(pos); // 删除 pos 位置的数据,返回下一个数据的位置remove(elem); // 删除容器中所有与 elem 值匹配的元素功能描述
函数原型
front(); // 返回第一个元素back(); // 返回最后一个元素注意不能使用 at 和 [] 的方式访问容器中的元素
原因是 list 本质是链表,而不是使用连续线性空间存储数据,迭代器也是不支持随机访问的
迭代器不支持随机访问,支持双向访问
功能描述
函数原型
reverse(); // 反转链表sort(); // 链表排序,其为成员函数所有不支持随机访问迭代器容器,不可以使用标准算法
不支持随机访问迭代器的容器,内部会提供对应一些算法
对于自定义数据类型,sort() 括号可以添加一个排序规则
高级排序只是在排序规则上再进行一次逻辑规则的制定,并不复杂
bool comparePerson(Person& p1, Person& p2){ // 按照年龄升序 if (p1.m_Age == p2.m_Name) { // 年龄相同,身高升序 return p1.Height > p2.Height; } else { return p1.m_Age < p2.m_Age; }}l.sort(comparePerson); // 排序算法
简介:
本质
set 和 multiset 区别
功能描述
函数原型
set<T> s; // 默认构造函数set(const set& s); // 拷贝构造函数set& operator=(const set& s); // 重载赋值运算符inset(elem); // 插入数据功能描述
函数原型
size(); // 返回容器中元素数目empty(); // 判断容器是否为空swap(s); // 交换两个集合容器 功能描述
函数原型
insert(elem); // 在容器中插入元素clear(); // 清空所有元素erase(pos); // 删除 pos 迭代器所指的元素,返回下一个元素的迭代器erase(beg, end); // 删除区间 [beg, end] 的所有元素,返回下一个元素的迭代器erase(elem); // 删除容器中值为 elem 的元素功能描述
函数原型
find(key); // 查找 key 是否存在,若存在,返回该键的元素的迭代器;若不存在,返回 set.end();count(key); // 统计 key 的元素个数掌握 set 和 multiset 的区别
区别
set 不可以插入重复数据,而 multiset 可以
set插入数据的同时会返回插入结果,表示插入是否成功
ret.second用来查看是否插入成功
multiset 不会检测数据,因此可以重复插入数据
功能描述
两种创建方式
pair<type1, type2> p (value1, value2); // 两个 type 分别对应 value 的数据类型pair<type1, type2> p = make_pair(value1, value2);两种创建方式,记住一种就可以了
使用方式
pair<string, int> p ("Tom", 20);cout << "name:" << p.first << " age:" << p.second;set 容器默认排序规则为从小到大,掌握如何改变排序规则
#include <set>class MyCompare{public: bool operator()(int v1, int v2) const { return v1 > v2; // 降序排序 }};void test(){ // 存放内置数据类型,改变排序规则 set<int, MyCompare> s1; // 仿函数的本质是一个类 s1.insert(10); s1.insert(30); s1.insert(20); s1.insert(40); for (set<int, MyCompare>::iterator it = s1.begin(); it != s1.end(); it++) { cout << *it << endl; }}对于自定义的数据类型,要创建排序规则,必须要指定排序规则
class MyCompare{public: bool operator()(const Person& p1, const Person& p2) { // 按照年龄降序 return p1.m_Age > p2.m_Age; }};
简介:
本质:
优点:
map/ multimap 区别
功能描述:
函数原型
map<T1, T2> mp; // map 默认构造函数map (const map& mp); // 拷贝构造map& operator=(const map& mp); // 重载赋值运算符map 容器中所有元素都是成对出现的,插入数据的时候要使用对组
功能描述:
函数原型
size(); // 返回容器中元素的数目empty(); // 判断容量是否为空swap(mp); // 交换两个集合容器功能描述:
函数原型
insert(elem); // 在容器中插入元素clear(); // 清空所有元素erase(pos); // 删除 pos 迭代器所指的元素,返回下一个元素的迭代器erase(beg, end); // 删除区间 [beg, end] 的所有元素,返回下一个元素的迭代器erase(key); // 删除容器键为 key 的元素注意插入的是对组
// 第一种m.insert(pair<int, int>(1, 10));// 第二种m.insert(make_pair(2, 20));// 第三种m.insert(map<int, int>::value_type(3, 30));// 第四种m[4] = 40; // 不建议使用,可以利用 [] 访问值
功能描述:
函数原型
find(); // 查找 key 是否存在,返回改键的元素的迭代值;若不存在,返回 m.end();count(); // 统计 key 的元素个数map 容器默认排序规则为按照键升序排序
其和 [set 排序](#8.8 set 排序)类似
class Worker{ // 创建员工public: string m_Name; int m_Salary;};void createWorker(vector<Worker>& v){ // 创建10名员工 for (int i = 0; i < 10; i++) { Worker worker; string nameSeed = "ABCDEFGHIJ"; worker.m_Name = "员工"; worker.m_Name += nameSeed[i]; worker.m_Salary = rand() % 10000 + 10000; // 10000~19999 v.push_back(worker); // 将员工放入分组中 }}void printWorker(const vector<Worker>& v){ for (vector<Worker>::const_iterator it = v.begin(); it != v.end(); it++) { cout << "name: " << it->m_Name << " salary: " << it->m_Salary << endl; }}void printWorker(multimap<string, Worker>& mp, string* arr){ string s0(20, '-'); for (int i = 0; i < 3; i++) { string s = arr[i]; s += "部门信息"; cout << s << endl; multimap<string, Worker>::iterator pos = mp.find(arr[i]); // 返回迭代器对象 int count = mp.count(arr[i]); for (int index = 0; pos != mp.end() && index < count; pos++, index++) { cout << "姓名:" << pos->second.m_Name << " 工资:" << pos->second.m_Salary << endl; } cout << s0 << endl; }}void setGroup(vector<Worker>& v, multimap<string, Worker>& mp, string* arr){ for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++) { // 产生随机部门编号 int depId = rand() % 3; // 0 1 2 随机数 // 将员工插入到分组中,key编号,value员工 mp.insert(pair<string, Worker>(arr[depId], *it)); }}void test(){ // 添加随机种子 srand((unsigned int)time(NULL)); string dep[] = { "策划", "美术", "研发" }; vector<Worker> v; createWorker(v); printWorker(v); // 员工分组 multimap<string, Worker> mp; setGroup(v, mp, dep); printWorker(mp, dep);}概念:
本质:
特点:
// 函数对象class MyAdd{public: MyAdd() { this->count = 0; } int operator()(int v1, int v2) { this->count++; return v1 + v2; } int count; // 函数对象可以有自己的内部状态};void test(){ MyAdd ma; cout << ma(1, 2) << endl; cout << ma(1, 2) << endl; cout << ma(1, 2) << endl; cout << ma(1, 2) << endl; cout << "调用次数为:" << ma.count << endl;}概念
class CreateFive{public: bool operator()(int val) { return val > 5 ? true : false; } // 一元谓词};void test(){ vector<int> v; for (int i = 0; i < 10; i++) { v.push_back(i); } // 查找容器中有么有大于5的数字 vector<int>::iterator it = find_if(v.begin(), v.end(), CreateFive()); // 其为匿名函数对象,find_if 的返回值为一个迭代对象 if (it == v.end()) { cout << "没有找到" << endl; } else { cout << "大于五的数字为:" << *it << endl; }}// 二元谓词class MySort{public: bool operator()(int a, int b) { return a > b ? true : false; }};void test(){ vector<int> v; v.push_back(10); v.push_back(20); v.push_back(30); v.push_back(40); v.push_back(50); // sort(v.begin(), v.end()); // 升序排列 // 使用函数对象,改变算法策略,变为排序规则降序排列 sort(v.begin(), v.end(), MySort()); for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << *it << " "; } cout << endl;}概念:
分类:
用法:
#include <functional>功能描述:
仿函数原理
template<class T> T plus<T>; // 加法运算template<class T> T minus<T>; // 减法运算template<class T> T multiplies<T>; // 乘法运算template<class T> T divides<T>; // 除法运算template<class T> T modulus<T>; // 取模运算template<class T> T negate<T>; // 取反运算,10 取反为 -10plus<int> p;cout << p(10, 20) << endl;negate<int> n;cout << n(20) << endl; 功能描述
仿函数原理
template<class T> bool equal_to<T>; // =template<class T> bool not_equal_to<T>; // !=template<class T> bool greater<T>; // >template<class T> bool greater_equal<T>; // >=template<class T> bool less<T>; // <template<class T> bool less_equal<T>; // <=功能描述
仿函数原理
template<typename T> bool logical_and<T>; // 与template<typename T> bool logical_or<T>; // 或template<typename T> bool logical_not<T>; // 非描述:
<algorithm><functional><numeric>组成<algorithm>是所有 STL 头文件中最大的一个,范围涉及到比较、交换、查找、遍历、赋值等等<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模块函数<functional>定义了一些模快类,用以声明函数对象学习目标:
算法简介:
for_each(); // 遍历容器transform(); // 搬运容器到另一个容器中功能描述:
函数原型
for_each(iterator beg, iterator end, _func);遍历算法:遍历容器元素
参数:
- beg:开始迭代器
- end:结束迭代器
- _func:函数或者函数对象,一般为输出内容的函数,回调函数
功能描述:
函数原型
transform(iterator beg1, iterator end1, iterator beg2, _func);目标容器要提前开辟空间,否则会报错
参数:
- beg1:源容器开始迭代器
- end1:源容器结束迭代器
- beg2:目标容器开始迭代器
- _func:函数或函数对象
学习目标:
算法简介:
find(); // 查找元素find_if(); // 按条件查找元素adjacent_find(); // 查找相邻重复元素binary_search(); // 二分查找法count(); // 统计元素个数count_if(); // 按条件统计元素个数功能描述:
函数原型
find(iterator beg, iterator end, value);参数:
- beg:开始迭代器
- end:结束迭代器
- value:查找的元素
如果是自定义数据类型,查找时要重载等号运算符
功能描述:
函数原型
find_if(iterator beg, iterator end, _Pred);参数:
- beg:起始迭代器
- end:结束迭代器
- _Pred:函数或者谓词(返回 bool 类型的仿函数)
功能描述:
函数原型
adjacent_find(iterator beg, iterator end);参数:
- beg:开始迭代器
- end:结束迭代器
功能描述:
函数原型
bool binary_search(iterator beg, iterator end, value);注意:在无序序列中不可用
参数:
- beg:开始迭代器
- end:结束迭代器
- value:查找的元素
功能描述:
函数原型
count(iterator beg, iterator end, value);beg:开始迭代器
end:结束迭代器
value:统计的元素
统计自定义数据类型时,要使用仿函数
class Person{public: Person(string name, int age) { this->m_Name = name; this->m_Age = age; } bool operator==(const Person& p) // 需要重载等号运算符,才可以统计 { return this->m_Age == p.m_Age && this->m_Name = p.m_Name? true : flase; } int m_Age; string m_Name;};统计自定义数据类型的时候,需要配合重载
operator==
功能描述:
函数原型
count_if(iterator beg, iterator end, _Pred);参数:
- beg:开始迭代器
- end:结束迭代器
- _Pred:谓词
学习目标
算法简介:
sort(); // 对容器内元素进行排序random_shuffle(); // 洗牌,指定范围内的元素随机调整次序merge(); // 容器元素合并,并存储到另一个容器中reverse(); // 反转指定范围内的元素功能描述
函数原型
sort(iterator beg, iterator end, _Pred);参数:
- beg:开始迭代器
- end:结束迭代器
- _Pred:谓词
功能描述
函数原型
random_shuffle(iterator beg, iterator end);使用时记得添加随机种子
srand((unsigned int)time(NULL));参数:
- beg:起始迭代器
- end:结束迭代器
功能描述:
函数原型
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);注意:
- 两个容器必须是有序的
- 要提前给目标容器分配空间
参数:
- beg1:容器1开始迭代器
- end1:容器1结束迭代器
- beg2:容器2开始迭代器
- end2:容器2结束迭代器
- dest:目标容器开始迭代器
功能描述
函数原型
reverse(iterator beg, iterator end);参数:
- beg:开始迭代器
- end:结束迭代器
学习目标
算法简介
copy(); // 容器内指定范围内的元素拷贝到另一个容器中replace(); // 将容器内指定范围的旧元素修改为新元素replace_if(); // 容器内指定范围满足条件的元素替换为新元素swap(); // 互换两个容器的元素功能描述:
函数原型
copy(iterator beg, iterator end, iterator dest);按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
需要先预定目标容器的空间
参数:
- beg:开始迭代器
- end:结束迭代器
- dest:目标容器起始迭代器
功能描述
函数原型
replace(iterator beg, iterator end, oldvalue, newvalue);它会替换区间内所有满足条件的元素
参数:
- beg:起始迭代器
- end:结束迭代器
- oldvalie:旧元素
- newvalue:新元素
功能用法
函数原理
replace_if(iterator beg, iterator end, _Pred, newvalue);它会替换区间内所有满足条件的元素
参数:
- beg:起始迭代器
- end:结束迭代器
- _Pred:谓词
- newvalue:替换的新元素
功能描述:
函数原型
swap(container c1, container c2);同种数据类型的容器才能互换
参数:
- c1:容器1
- c2:容器2
学习目标
注意:
#include <numeric>算法简介
accumulate(); // 计算容器元素累计总和fill(); // 向容器中添加元素功能描述:
函数原型
accumulate(iterator beg, iterator end, firstValue);参数:
- beg:起始迭代器
- end:结束迭代器
- firstValue:起始累加值
功能描述:
函数原型
fill(iterator beg, iterator end, value);参数:
- beg:开始迭代器
- end:结束迭代器
- value:填充的值
学习目标:
算法简介
set_insersection(); // 求两个容器的交集set_union(); // 求两个容器的并集set_different(); // 求两个容器的差集功能描述:
函数原型
iterator itEnd = set_insersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); // 交集,返回最后一个值的迭代器需要提前开辟空间,最特殊的情况:大容器包含小容器,开辟空间,取小容器的 size 即可
dest.resize(c1.size() > c2.size() ? c2.size() : c1.size());参数:
- beg1:容器1开始迭代器
- end1:容器1结束迭代器
- beg2:容器2开始迭代器
- end2:容器2结束迭代器
- dest:目标容器开始迭代器
功能描述:
函数原型
iterator itEnd = set_insersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); // 并集,返回最后一个值的迭代器目标容器要提前开辟空间,最特殊的情况是两个容器没有交集
dest.resize(c1.size() + c2.size());参数:
- beg1:容器1开始迭代器
- end1:容器1结束迭代器
- beg2:容器2开始迭代器
- end2:容器2结束迭代器
- dest:目标容器开始迭代器
功能描述:
函数原型
iterator itEnd = set_insersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); // c1 和 c2 的差集,返回最后一个值的迭代器,c1 - c2目标容器要提前开辟空间,最特殊的情况是两个容器没有交集,取 size 大的作为容器的空间
dest.resize(c1.size() > c2.size() ? c1.size() : c2.size()); // 也可以使用 max(c1.size(), c2.size());参数:
- beg1:容器1开始迭代器
- end1:容器1结束迭代器
- beg2:容器2开始迭代器
- end2:容器2结束迭代器
- dest:目标容器开始迭代器
本文来自博客园,作者:A-L-Kun,转载请注明原文链接:https://www.cnblogs.com/liuzhongkun/p/15910727.html