C++高级编程 -知识点笔记(自用)

C++高级编程

1 模板(泛型编程)

1.1 模版的概念

模板就是建立通用的模具,大大提高复用性

1.2 函数模板
  • C++的另一种编程思想称为泛型编程,主要利用的技术就是模板
  • C++的两种模板机制:函数模板类模板
1.2.1 函数模板语法

函数模板作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

template<typename T>
函数声明或定义

解释:

template — 声明创建模板

typename — 表明其后面的符号是一种数据类型,可以用class代替

**T ** — 通用的数据类型,名称可以替换,通常都是大写字母

//函数模板
template<typename T>  //声明一个模板,T是一个通用数据类型
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}
void test01() {
	int a = 10;
	int b = 20;
	//两种方式使用函数模板
	//1.自动类型推导
	//mySwap(a, b);
	//2.显示指定类型
	mySwap<int>(a, b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
}
int main() {
	test01();
	system("pause");
	return 0;
}

总结:

  • 函数模板利用关键字template
  • 使用函数模板有两种方式,自动类型推导、显示指定类型
  • 模板的目的是提高复用性,将类型参数化
1.2.2 函数模板注意事项
  • 自动类型推导,必须是推导一致的数据类型T,才可以使用。
  • 模板必须确定出T的数据类型,才可以使用
template<typename T>  //声明一个模板,T是一个通用数据类型
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}
//1.自动类型推导,必须是推导一致的数据类型T才可以使用。
void test01() {
	int a = 10;
	int b = 20;
    char c = 'd';
	//1.自动类型推导
	mySwap(a, b);
    //mySwap(a,c); //错误,推导不出一致的数据类型
	cout << "a=" << a << endl;
}
//2.模板必须确定出T的数据类型,才可以使用
template<class T>
void func(){
    cout<<"cout调用"<<endl;
}

int main() {
	test01();
    //func();//报错, 该函数模板没指定出T的数据类型
    func<int>();//显示指定类型,随便给T指定一个数据类型, 就能运行起来了
	system("pause");
	return 0;
}
1.2.3 函数模板案例
  • 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
  • 排序规则从大到小,排序算法为选择排序
  • 分别利用char数组和int数组进行测试
//交换模板
template<typename T>
void mySwap(T &a,T &b) {
	T temp = a;
	a = b;
	b = temp;
}
//选择排序模板
template<typename T>
void mySort(T arr[],int len) 
{
	for (int i = 0; i < len; i++) {
		int max = i;
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j]) {
				max = j;   //因为是从大到小排序,每次都找最大值交换
			}
		}
		if(max!=i){
			mySwap(arr[max], arr[i]);
		}
	}
}
//打印的模板
template<typename T>
void printArray(T arr[],int len){
	for (int i = 0; i < len; i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}

void test01() {
	char charArr[] = "badcfe";
	int len = sizeof(charArr) / sizeof(char);
	mySort(charArr, len);
	printArray(charArr, len);
	
}
void test02() {
	int intArr[] = { 7,5,1,3,9,2,4,6,8 };
	int len = sizeof(intArr) / sizeof(intArr[0]);
	mySort(intArr, len);
	printArray(intArr, len);
}
int main() {
	//test01();
	test02();	
	system("pause");
	return 0;
}
1.2.4 普通函数和函数模板区别
  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发送隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换
//普通函数
int myAdd01(int a,int b) {
	return a + b;
}
//函数模板
template<typename T>
T myAdd02(T a, T b) {
	return a + b;
}

void test01() {
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << myAdd01(a, c) << endl;//普通函数调用时可以发生隐式类型转换
	//自动类型推导
	myAdd02(a, b);
	//myAdd02(a, c); //报错,如果利用自动类型推导,不会发送隐式类型转换
	
	//显示指定类型
	myAdd02<int>(a, b);
	//如果利用显示指定类型的方式,可以发生隐式类型转换
	myAdd02<int>(a, c);//明确告诉编译器如果不是int类型的转为int类型
}
1.2.5 普通函数与函数模板的调用规则

调用规则:

  1. 如果函数模板和普通函数都可以实现,则优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发送重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板
void myPrint(int a,int b) {
	cout << "调用普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b)   //函数模板允许发生重载
{
	cout << "调用的模板" << endl;

}
//函数模板也可以发送重载
template<typename T>
void myPrint(T a, T b, T c)  
{
	cout << "调用的重载模板" << endl;

}

void test01() {
	int a = 10;
	int b = 20;
	//如果函数模板和普通函数都可以实现,则优先调用普通函数
	myPrint(a, b);//此时会调用普通函数

	//通过空模板参数列表来强制调用函数模板
	myPrint<>(a, b);

	//调用重载的函数模板
	myPrint(a, b, 100);

	//如果函数模板可以产生更好的匹配,优先调用函数模板
	char c = 'c';
	char d = 'd';
	myPrint(c, d);//编译器会觉得强转为int型比较麻烦,直接使用可以匹配的模板
}

总结:既然提供了函数模板,最好不要提供相同功能的普通函数,否则容易出现二义性

1.2.6 模板的局限性
  • 模板的通用性并不是万能的

为了解决T的数据类型传入的是像Person这样的自定义数据类型的问题,C++提供模板的重载,可以为这些特定的类型提供具体化的模板

class Person {
public:
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};
//对比两个数据是否相等,常规函数模板
template<typename T>
bool myCompare(T &a, T &b) {
	if (a == b) {
		return true;
	}
	else {
		return false;
	}
}

//利用具体化Person的版本实现代码,具体化优先调用
//template<> 声明这是一个具体化数据类型的函数模板
template<> bool myCompare(Person& p1, Person& p2) { //myCompare函数模板重载
	if (p1.m_Age == p2.m_Age && p1.m_Name == p2.m_Name) {
		return true;
	}
	else {
		return false;
	}
}

void test02() {
	Person p1("Tom", 11);//看到是Person,优先走具体化Person的函数模板
	Person p2("Tom", 10);
	bool ret = myCompare(p1, p2);
	if (ret) {
		cout << "p1==p2" << endl;
	}
	else {
		cout << "p1!=p2" << endl;
	}
}

总结:

  • 利用具体化的模板,可以解决自定义类型的通用化
  • 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
1.3 类模板
1.3.1 类模板语法

类模板作用:

  • 建立一个通用类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型来代表

语法:

template<typename T>
类

示例:

//类模板
template<typename NameType,typename AgeType>
class Person {
public:
	Person(NameType name,AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson() {
		cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};
void test01() {
	Person<string, int> p1("Tom", 18);//<string,int>是模板参数列表
	p1.showPerson();
}
1.3.2 类模板与函数模板区别
  1. 类模板没有自动类型推导的使用方式,只能用显示指定类型方式!
  2. 类模板在模板参数列表中可以有默认参数
//类模板

template<typename NameType,typename AgeType>
//template<typename NameType,typename AgeType=int> //在模板参数列表中可以有默认参数
class Person {
public:
	Person(NameType name,AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson() {
		cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};
void test01() {
    //Person p1("Tom",18);//报错,类模板没有自动类型推导的使用方式
	Person<string, int> p1("Tom", 18);//<string,int>是模板参数列表
    //在模板参数列表中可以有默认参数
	Person<string> p2("猪八戒", 999);//因为AgeType有默认参数int,所以不用写
    
	p1.showPerson();
}
1.3.3 类模板成员函数创建时机
  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建
class Person1 {
public:
	void showPerson1() {
		cout << "Person1 show" << endl;
	}
};
class Person2 {
public:
	void showPerson2() {
		cout << "Person2 show" << endl;
	}
};
//创建一个类模板
template<typename T>
class Myclass {
public:
	T obj;//当T是Person数据类型时,看作是实例化一个对象
	//类模板中的成员函数在调用时才创建
	void func1() {
		obj.showPerson1();
	}
	void func2() {
		obj.showPerson2();
	}
};
void test01() {
	Myclass<Person1> m;//实例化对象m
	m.func1();
	//m.func2();//报错,Person1的对象无法访问Person2的成员函数
}
1.3.4 类模板对象做函数参数

类模板实例化出的对象,向函数传参的方式:

  1. 指定传入的类型 — 直接显示对象的数据类型
  2. 参数模板化 — 将对象的参数变为模板进行传递
  3. 整个类模板化 — 将这个对象类型 模板化进行传递
//类模板
template<typename T1, typename T2>
class Person {
public:
	Person(T1 name, T2 age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson() {
		cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
	}

	T1 m_Name;
	T2 m_Age;
};
//1.指定传入的类型(最常用)
void printPerson1(Person<string, int>& p) {
	p.showPerson();
}
void test01() {
	Person<string, int> p("孙悟空", 100);
	printPerson1(p);
	
}
//2.参数模板化
template<typename T1,typename T2>
void printPerson2(Person<T1,T2> &p) {
	p.showPerson();
	//如何看模板中参数列表推导出的具体参数类型
	cout << typeid(T1).name() << endl;//输出string
}
void test02() {
	Person<string, int> p("猪八戒", 100);
	printPerson2(p);
}
//3.整个类模板化
template<typename T>
void printPerson3(T & p) {//将整个Person类看做一个数据类型,整个用T表示
	p.showPerson();
}
void test03() {
	Person<string, int> p("唐僧", 30);
	printPerson3(p);
}
1.3.5 类模板与继承

注意以下几点:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定父类中T的类型
  • 如果不指定,编译器无法给予子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需变为类模板
template<class T>
class Base{
    T m;
};
//class Son:public Base;//报错,必须要知道父类中的T类型,才能继承给子类
class Son:public Base<int>{
    
};
void test01(){
    Son s1;
}
//如果想灵活指定父类中T类型,子类也需要变类模板
template<class T1,class T2>
class Son2:public Base<T2>{
public:
    T1 obj;
};
void test02(){
    Son2<int ,char> s2;//实例化子类
}
1.3.6 类模板成员函数类外实现
//类模板成员函数类外实现
template<typename T1, typename T2>
class Person {
public:
	Person(T1 name, T2 age);//函数声明
	/*{
		this->m_Name = name;
		this->m_Age = age;
	}*/
	void showPerson();//函数声明
	/*{
		cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
	}*/

	T1 m_Name;
	T2 m_Age;
};
//类模板构造函数类外实现
template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age) {//记得加上模板参数列表<>
	this->m_Name = name;
	this->m_Age = age;
}
//类模板成员函数类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() //记得加上模板参数列表<>
{
	cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
}
1.3.7 类模板分文件编写

类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到,解决方案:

  1. 直接包含.cpp源文件
  2. 将声明和实现写到同一个文件中,并更改后缀名为.hpp, hpp是约定的名称,并不是强制

.h文件

#pragma once
#include<string>
template<typename T1, typename T2>
class Person {
public:
	Person(T1 name, T2 age);//函数声明
	void showPerson();//函数声明
	T1 m_Name;
	T2 m_Age;
};

.cpp文件

#include<iostream>
using namespace std;
#include "Person.h"
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {//记得加上模板参数列表<>
	this->m_Name = name;
	this->m_Age = age;
}
//类模板成员函数类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
}

main函数文件

#include "Person.cpp"
void test01() {
	Person<string,int> p("Tom", 18);
	p.showPerson();
}
int main() {
	test01();
	system("pause");
	return 0;
}

或者将声明和实现写到同一个文件中,并更改后缀名为.hpp(主流解决方法)

.hpp文件,声明实现都在一个文件中,一看就知道是类模板

#pragma once
#include<string>
template<typename T1, typename T2>
class Person {
public:
	Person(T1 name, T2 age);//函数声明
	void showPerson();//函数声明
	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {//记得加上模板参数列表<>
	this->m_Name = name;
	this->m_Age = age;
}
//类模板成员函数类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
}
1.3.8 类模板与友元

全局函数类内实现-- 直接在类内声明友元即可

全局函数类外实现-需要提前让编译器知道全局函数的存在

//全局函数,类外实现
template<class T1,class T2>
class Person;
template<class T1, class T2>
void printPerson2(Person<T1, T2> p) {
	cout << "类外实现---姓名:" << p.m_Name << "年龄:" << p.m_Age << endl;
}
template<class T1,class T2>
class Person {
	//全局函数,类内实现
	friend void printPerson(Person<T1,T2> p) {
		cout << "姓名:" << p.m_Name << "年龄:" << p.m_Age << endl;
	}
	//全局函数,类外实现
	friend void printPerson2<>(Person<T1, T2> p);

public:
	Person(T1 name, T2 age) {
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;

};
void test01() {
	Person<string, int> p("Tom", 18);
	printPerson(p);
}

void test02() {
	Person<string, int> p("jack", 18);
	printPerson2(p);
}
1.3.9 类模板案例

案例描述:实现一个通用的数组类,要求如下:

  • 可以对内置数据类型以及自定义数据类型的数据进行存储
  • 将数组中的数据存储在堆区
  • 构造函数中可以传入数组的容量
  • 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
  • 提供尾插法和尾删法对数组中的数据进行增加和删除
  • 可以通过下标的方式访问数组中的元素
  • 可以获取数组中当前元素个数和数组的容量

MyArray.hpp文件

#pragma once
#include<iostream>
using namespace std;
#include<string>

//通用数组类
template<class T>
class myArray {
public:
	//有参构造 参数为容量
	myArray(int capacity) {
		this->m_Capacity = capacity;
		this->m_Size = 0;//初始化元素个数
		this->pAddress = new T[capacity];
	}
	//拷贝构造
	myArray(const myArray& arr) {
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		//this->pAddress =arr.pAddress //这是个指针,浅拷贝会导致堆区数据重复释放问题,需用深拷贝
		//深拷贝
		this->pAddress = new T[arr.m_Capacity];
		//将数组arr中的数据都拷贝过来
		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}
	}
	//重载operator= 防止浅拷贝问题
	myArray& operator=(const myArray & arr) {
		//先判断原来堆区是否有数据,如果有先释放
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}
		//进行深拷贝
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		this->pAddress = new T[arr.m_Capacity];
		//将数组arr中的数据都拷贝过来
		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}
		return *this;
	}


	
	//尾插法
	void Push_Back(const T & val) {
		if (this->m_Capacity == this->m_Size) {
			return;
		}
		this->pAddress[this->m_Size] = val;
		this->m_Size++;//更新数组大小

	}
	//尾删法
	void Pop_Back() {
		//让用户访问不到最后一个元素,即为尾删,做逻辑上的删除
		if (this->m_Size == 0) {
			return;
		}
		this->m_Size--;
	}
	//通过下标的方式访问数组中的数据 arr[0]=100,当重载运算符做左值时应该使用引用类型返回
	T& operator[](int index) {
		return this->pAddress[index];
	}
	//返回数组容量
	int getCapacity() {
		return this->m_Capacity;
	}
	//返回数组大小
	int getSize() {
		return this->m_Size;
	}

	//析构函数
	~myArray() {
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
	}
private:
	T* pAddress;
	int m_Capacity;
	int m_Size;
};

main.cpp文件

#include "MyArray.hpp"
void printArray(myArray<int> &arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i] << endl;
	}
}
void test01() {
	myArray<int> arr1(5);
	for (int i = 0; i < 5; i++) {
		arr1.Push_Back(i);
	}
	printArray(arr1);
	//拷贝复制
	myArray<int> arr2(arr1);
	//对arr2进行尾删
	arr2.Pop_Back();
	cout << "尾删后:" << endl;
	cout << "arr2容量为:"<<arr2.getCapacity() << endl;
	cout << "arr2大小为" << arr2.getSize() << endl;
}
//测试自定义数据类型
class Person {
public:
	Person() {

	}
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;

	}
	string m_Name;
	int m_Age;
};
void printPersonArray(myArray<Person> & arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << "姓名" << arr[i].m_Name << "年龄" << arr[i].m_Age << endl;//arr[i]返回的是一个Person对象
	}
}
void test02() {
	myArray<Person> arr(10);//会调用无参构造
	Person p1("刘一", 18);
	Person p2("刘2", 19);
	Person p3("刘3", 13);
	Person p4("刘4", 14);
	Person p5("刘5", 15);
	arr.Push_Back(p1);
	arr.Push_Back(p2);
	arr.Push_Back(p3);
	arr.Push_Back(p4);
	arr.Push_Back(p5);
	printPersonArray(arr);
	cout << "arr容量为:" << arr.getCapacity() << endl;
	cout << "arr大小为" << arr.getSize() << endl;
}
int main() {

	//test01();
	test02();
	system("pause");
	return 0;
}

2 STL初识

2.1 STL的诞生
  • C++的面向对象和泛型编程思想,目的就是提升代码复用性
  • 为了建立数据结构和算法的一套标准,诞生了STL
2.2 STL基本概念
  • STL(Standard Template Library,标准模板库)
  • STL从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
  • 容器算法之间通过迭代器进行无缝连接
  • STL几乎所有的代码都采用了模板类或者模板函数
2.3 STL六大组件

STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
  2. 算法:各种常用的算法,如sort、find、copy、for_each等。
  3. 迭代器:扮演了容器与算法之间的胶合剂。
  4. 仿函数:行为类似函数,可作为算法的某种策略。
  5. 适配器:一种用来修饰容器或者仿函数或者迭代器接口的东西。
  6. 空间配置器:负责空间的配置与管理。
2.4 STL中容器、算法、迭代器

容器:

STL容器就是将运用最广泛地一些数据结构实现出来。

常用的数据结构:数组,链表,树,栈,队列,集合,映射表等

这些容器分为序列式容器和关联式容器两种:

  • 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
  • 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。

算法:

算法分为:质变算法和非质变算法。

质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等

非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找,计数,遍历,寻找极值等

迭代器:

容器和算法之间的粘合剂。每个容器都有自己专属的迭代器,迭代器使用非常类似于指针,初学阶段先理解迭代器为指针。

迭代器种类:
在这里插入图片描述

常用的容器中迭代器种类为双向迭代器,和随机访问迭代器。

2.5 容器算法迭代器初识

STL中最常用的容器为Vector,可以理解为数组。

2.5.1 vector存放内置数据类型

容器:vector

算法:for_each

迭代器:vector: : iterator

#include<iostream>
using namespace std;
#include<string>
#include<vector>
#include<algorithm>

void myPrint(int val) {
	cout << val << endl;
}

void test01() {
	//创建一个vector容器,数组
	vector<int> v;
	//向容器中插入数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);
	//通过迭代器访问容器中的数据
	vector<int>::iterator itBegin = v.begin();//起始迭代器,指向容器中第一个元素,即用itBegin指针指向第一个元素的地址
	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;
	}
	//第三种遍历方式  利用STL提供的遍历算法
	for_each(v.begin(), v.end(), myPrint);//利用类似回调技术打印容器中的数据

}
2.5.2 vector存放自定义数据类型
class Person {
public:
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

void test01() {
	vector<Person> v;
	Person p1("a",10);
	Person p2("b", 20);
	Person p3("c", 30);
	Person p4("d", 40);
	//向容器添加数据
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	//遍历容器中数据
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {//*it解引用后表示一个具体的对象
		cout << "姓名"<<(*it).m_Name <<"年龄" <<(*it).m_Age << endl;
		//it看做是一个结构体指针,所以也可以用->访问属性
		cout << "姓名" << it->m_Name << "年龄" << it->m_Age << endl;
		
	}
}
//存放自定义数据类型,指针
void test02() {
	vector<Person*> v;
	Person p1("a", 10);
	Person p2("b", 20);
	Person p3("c", 30);
	Person p4("d", 40);
	//向容器添加数据
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	//遍历容器中数据
	//一个技巧:*it解引用出来的数据类型跟<Person>尖括号里的类型保持一致。
	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {//*it迭代得到的是Person类型指针
		cout << "姓名" << (*it)->m_Name << "年龄" << (*it)->m_Age << endl;
	}
}
2.5.3 vector容器嵌套容器
//大容器嵌套小容器
void test01() {
	vector<vector<int>> v;
	//创建小容器
	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	vector<int> v4;
	//在小容器中添加数据
	for (int i = 0; i < 4; i++) {
		v1.push_back(i);
		v2.push_back(i+1);
		v3.push_back(i+2);
		v4.push_back(i+3);
	}
	//将小容器放进大容器
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);
	//遍历大容器中的所有数据
	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
		//这里*it实际上代表一个容器数据类型,所有还得进一步遍历
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
			cout << *vit << " ";
		}
		cout << endl;
	}

}

3 STL-常用容器

3.1 string容器
3.1.1 string基本概念

本质

  • string是C++风格的字符串,而string本质上是一个类

string和char*区别:

  • char*是一个指针
  • string是一个类,类内部封装了char* ,管理这个字符串,是一个char*型的容器

特点:

string类内部封装了很多成员方法,例如:查找find、拷贝copy、删除delete、替换replace、插入insert.

string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责。

3.1.2 string构造函数

构造函数原型:

  • string(); //创建一个空的字符串

  • string(const char* s); //使用字符串s初始化

  • string(const string& str); //使用一个string对象初始化另一个string对象,拷贝构造函数

  • string(int n,char c); //使用n个字符c初始化

string s1;//创建空字符串,调用无参构造函数

const char * str = "hello world";
string s2(str);

string s3(s2);//调用拷贝构造

string s4(10,'a');
3.1.3 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 str1;
str1 = "hello world";

str2 = str1;

string str3;
str3 = 'a';

string str4;
str4.assign("hello C++");

string str5;
str5.assign("hello C++",5);//输出hello

string str6;
str6.assign(str5);//类似拷贝构造函数
3.1.4 string字符串拼接
  • 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) ;
  • string& append(const string &s, int pos, int n); //字符串s中从pos开始的n个字符连接到字符串结尾
string str1="我";
str1+="爱玩儿游戏";//追加一个字符串

string str3="I";
str3.append(" love ");//输出 I love

str3.append("game abcde",4);//把字符串s的前4个字符连接到当前字符串结尾
3.1.5 string查找和替换
  • 查找:查找指定字符串是否存在
  • 替换:在指定的位置替换字符串

函数原型:

在这里插入图片描述

string str1="abcdefgde";
int pos = str1.find("de");//pos=3,没找到则返回pos=-1;

//rfind和find区别:rfind是从右往左查找,find是从左往右
pos =str1.rfind("de");//pos=7

string str2= "abcde";
str2.replace(1,3,"1111");//替换从位置1开始的3个字符为字符串1111,输出a1111e
3.1.6 string字符串比较

比较方式:

  • 字符串比较是按字符的ASCII码进行对比

= 返回 0;> 返回 1;< 返回 -1;

函数原型:

  • int compare(const string &s) const;
  • int compare(const char * s) const;
string str1 = "hello";
string str2 = "hello";
if(str1.compare(str2)==0){
    cout<<"str1等于str2"<<endl;
}
3.1.7 string 字符存取

string中单个字符存取方式有两种:

  • char& operator[] (int n);
  • char& at(int n);
string str="hello";
//1.通过[]访问单个字符
for(int i=0;i<str.size();i++){
    cout<<str[i]<<endl;
}
//2.通过at方式访问单个字符
for(int i=0;i<str.size();i++){
    cout<<str.at(i)<<endl;
}

//修改单个字符
str[0]='x';
cout<<str<<endl;//xello

str.at(1)='x';
cout<str<<endl;//xxllo
3.1.8 string插入和删除

功能描述:对string字符串进行插入和删除操作。

函数原型:

在这里插入图片描述

string str="hello";
//插入
str.insert(1,"111");//输出h111ello
//删除
str.erase(1,3);//输出hello

3.1.9 string 子串

函数原型;

  • string substr(int pos=0,int n=npos) const; //返回由pos开始的n个字符组成的字符串
string str = "abcdef";
string str2 = str.substr(1,3);//输出子串bcd

string email ="zhangsan@sina.com";
//截取用户名
int pos =email.find('@');
string usrName = email.substr(0,pos);//输出zhangsan
3.2 vector容器
3.2.1 vector基本概念

功能:vector数据结构和数组非常相似,也称为单端数组

vector与普通数组区别:

  • 不同之处在于数组是静态空间,而vector可以动态扩展。

动态扩展:

  • 并不是在原有空间之后续接新空间,而是找更大空间,然后将原数据拷贝到新空间,之后释放原空间。
  • vector容器的迭代器是支持随机访问的迭代器。
3.2.2 vector构造函数

函数原型:
在这里插入图片描述

#include<vector>
void printVector(vector<int> &v) {
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it <<" ";
	}
	cout << endl;
}
void test01() {
	vector<int> v1;//1.默认构造
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);

	//2.通过区间方式构造
	vector<int> v2(v1.begin(), v1.end());
	printVector(v2);
	
	//3.n个elem方式构造,构造函数将10个100拷贝给本身
	vector<int> v3(10, 100);
	printVector(v3);

}
3.2.3 vector赋值操作

函数原型:
在这里插入图片描述

#include<vector>
void printVector(vector<int>& v) 
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it <<" ";
	}
	cout << endl;
}
void test01() {
	vector<int> v1;//1.默认构造
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);
	//赋值 operator=
	vector<int> v2;
	v2 = v1;
	printVector(v2);
	//assign
	vector<int> v3;
	v3.assign(v1.begin(), v1.end());//左闭右开
	printVector(v3);

	//n个elem方式
	vector<int> v4;
	v4.assign(10, 100);
	printVector(v4);
}
3.2.4 vector容量和大小

函数原型:

在这里插入图片描述

void printVector(vector<int>& v) 
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it <<" ";
	}
	cout << endl;
}
void test01() {
	vector<int> v1;//1.默认构造
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);

	if (v1.empty()) {//为真,代表容器为空
		cout << "v1为空" << endl;

	}
	else {
		cout << "v1不为空" << endl;
		cout << "v1容量为" << v1.capacity() << endl;
		cout << "大小为:" << v1.size() << endl;
	}
	//v1.resize(15);//如果重新指定的比原来size长,默认用0填充
	v1.resize(15,100);//利用重载版本,可以指定默认填充值为100
	printVector(v1);

	v1.resize(5);
	printVector(v1);//如果重新指定size的比原来短,超出部分会删除

}
3.2.5 vector插入和删除

函数原型:

在这里插入图片描述

void printVector(vector<int>& v) 
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it <<" ";
	}
	cout << endl;
}
void test01() {
	vector<int> v1;//1.默认构造

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	printVector(v1);
	//尾删
	v1.pop_back();
	printVector(v1);
	//在迭代器指向位置插入100
	v1.insert(v1.begin(), 100);
	printVector(v1);
	//在迭代器指向位置插入2个1000
	v1.insert(v1.begin(),2,1000);
	printVector(v1);//1000,1000,100,10,20,30,40
	//删除迭代器指向的起始元素
	v1.erase(v1.begin());
	
	//删除迭代器从start到end之间的元素,类似清空操作
	v1.erase(v1.begin(),v1.end());
	//清空
	v1.clear();

}

总结:

  • 尾插 — push_back
  • 尾删 — pop_back
  • 插入 — insert(位置迭代器)
  • 删除 — erase(位置迭代器)
3.2.6 vector数据存取

函数原型:

在这里插入图片描述

void test(){
    vector<int> v1;
    for(int i=0;i<10;i++){
        v1.push_back(i);
    }
    //利用[]方式访问元素
    for( int i=0;i<v1.size();i++){
       cout<<v1[i]<<endl;
    }
    //利用at方式访问
     for( int i=0;i<v1.size();i++){
       cout<<v1.at(i)<<endl;
    }
    //获取第一个元素
    cout<<v1.front()<<endl;
    //获取最后一个元素
    cout<<v1.back()<<endl;
}
3.2.7 vector互换空间

功能描述:

  • 实现两个容器内元素进行互换

函数原型:

  • vector1.swap(vector2) ; //将vec与本身元素互换
void printVector(vector<int>& v) 
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it <<" ";
	}
	cout << endl;
}
void test01() {
	vector<int> v1;//1.默认构造
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);
	vector<int> v2;//1.默认构造
	for (int i = 10; i > 0; i--) {
		v2.push_back(i);
	}
	printVector(v2);
	cout << "交换后" << endl;
	v1.swap(v2);
	printVector(v1);
	printVector(v2);

}

//2.实际用途,空间收缩
//巧用swap可以收缩内存空间
void test02() {
	vector<int> v;//1.默认构造
	for (int i = 0; i < 10000; i++) {
		v.push_back(i);
	}
	cout << "v的容量为:" << v.capacity()<<endl;//13825
	cout << "v的大小为:" << v.size() << endl;//10000

	v.resize(3);//重新指定大小,产生浪费空间的问题
	cout << "v的容量为:" << v.capacity() << endl;//13825
	cout << "v的大小为:" << v.size() << endl;//3
	//巧用swap收缩内存,解决问题
	//vector<int>(v)是一个匿名对象,当前行结束便被回收
	//按照v的大小来初始化当前匿名对象,它的大小和容量都为3
	vector<int>(v).swap(v);  //容器交换,类似指针交换
	cout << "v的容量为:" << v.capacity() << endl;//3
	cout << "v的大小为:" << v.size() << endl;//3
}
3.2.8 vector 预留空间

功能描述:

  • 减少vector在动态扩展容量时的扩展次数

函数原型:

  • reserve(int len) ; //容器预留len个元素长度,预留位置不初始化,元素不可访问
void test02() {
	vector<int> v;//1.默认构造

	//利用reserve预留空间
	v.reserve(10000);

	int num = 0;//用于统计内存开辟次数
	int* p = NULL;
	for (int i = 0; i < 10000; i++) {
		v.push_back(i);
		if (p != &v[0]) {//如果指针没有指向容器第一个元素地址
			p = &v[0];
			num++;
		}
	}
	cout << "num=" << num << endl;//如果不预留空间需要开辟24次,预留后就一次

}
3.3 deque容器(双向队列)
3.3.1 deque容器基本概念

功能:

  • 双端数组,可以对两端进行插入删除操作

deque和vector区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对而言,对头部的插入删除速度会比deque快
  • vector访问元素时的速度会比deque快,这和两者内部实现有关

在这里插入图片描述

3.3.2 deque构造函数
#include<deque>

void printDeque(const deque<int> d) {
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		//*it=100;//一般为了不让容器中的元素遍历时被修改,使用const修饰即可
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	deque<int> d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);
	//通过区间拷贝赋值
	deque<int> d2(d1.begin(), d1.end());
	printDeque(d2);

	//构造函数将n个elem拷贝给本身
	deque<int> d3(10, 100);
	printDeque(d3);
	//拷贝构造
	deque<int> d4(d3);
	printDeque(d4);
}
3.3.3 deque赋值操作

函数原型:(与vector操作相同)

在这里插入图片描述

void printDeque(const deque<int>& d) {
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		//*it=100;//一般为了不让容器中的元素遍历时被修改,使用const修饰即可
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	deque<int> d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);
	//利用operator=赋值
	deque<int> d2;
	d2 = d1;
	//assign赋值
	deque<int> d3;
	d3.assign(d1.begin(), d1.end());

	deque<int> d4;
	d4.assign(10, 100);
}
3.3.4 deque大小操作

函数原型:

在这里插入图片描述

deque因为本身数据结构的原因,不存在capacity()这个接口。

3.3.5 deque插入和删除

函数原型:

在这里插入图片描述

3.3.6 deque数据存取

函数原型:(跟vector一样)

在这里插入图片描述

void printDeque(const deque<int>& d) {
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		//*it=100;//一般为了不让容器中的元素遍历时被修改,使用const修饰即可
		cout << *it << " ";
	}
	cout << endl;
}
//数据存取
void test01(){
    deque<int> d1;
    d1.push_back(10);
    d1.push_back(20);
    d1.push_front(100);
    d1.push_front(200);
    
    for(int i=0;i<d1.size();i++){
        cout<<d1[i]<<endl;
    }
     for(int i=0;i<d1.size();i++){
        cout<<d1.at(i)<<endl;
    }
    cout<<d1.front()<<endl;
    cout<<d1.back()<<endl;
}
3.3.7 deque排序

利用算法实现对deque容器进行排序。

对于支持随机访问的迭代器的容器,都可以利用sort算法来直接排序。比如vector容器也支持使用sort排序。

算法:(sort默认升序)

  • sort(iterator begin, iterator end); //对begin到end区间元素进行排序
#include<deque>
#include<algorithm>
void printDeque(const deque<int> &q){
    for(deque<int>::const_iterator it=q.begin();it!=q.end();it++){
        cout<<*it<<" ";
    }
    cout<<endl;
}
void test01(){
    deque<int> d1;
    d1.push_back(10);
    d1.push_back(20);
    d1.push_back(30);
    d1.push_front(100);
    d1.push_front(200);
    d1.push_front(300);
    printDeque(d1);
    //排序,默认排序规则为从小到大,升序
    sort(d1.begin(),d1.end());//使用算法时记得引入头文件algorithm
    
    
}

3.4 案例-评委打分

案例描述:

有5名选手:选手ABCDE,10个评委分别对每一位选手打分,去除最高分和最低分,取平均分。

实现步骤:
在这里插入图片描述

#include<deque>
#include<vector>
#include<algorithm>
#include<ctime>

//选手类
class Person {
public:
	Person(string name,int score) {
		this->m_Name = name;
		this->m_Score = score;
	}
	
	string m_Name;
	int m_Score;

};
void createPerson(vector<Person> &v) {
	string nameSeed = "ABCDE";
	for (int i = 0; i < 5; i++) {
		string name = "选手";
		name += nameSeed[i];
		int score = 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<int> d;
		for (int i = 0; i < 10; i++) {
			int score = rand() % 41 + 60;//rand()%41可以生成0-40的随机数
			d.push_back(score);
		}
		//显示选手的打分
		cout << "选手:" << it->m_Name << "打分:" << endl;
		for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++) {
			cout << *dit << " ";
		}
		cout << endl;

		//先排序,再去掉最高和最低
		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 average = sum / d.size();
		it->m_Score = average;
	}
}
void showScore(vector<Person> &v) {
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
		cout << it->m_Name << "的分数是:" << it->m_Score << endl;
	}

}

int main() {
	//随机数种子
	srand((unsigned int)time(NULL));
	
	//创建5名选手
	vector<Person> v;
	createPerson(v);
	//给5名选手打分
	setScore(v);
	//显示最后得分
	showScore(v);
	system("pause");
	return 0;
}
3.5 stack容器(栈)
3.5.1 stack基本概念

概念:stack是一种先进后出的数据结构,栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为。

3.5.2 stack常用接口

在这里插入图片描述

void test(){
    stack<int> s;
    //入栈
    s.push(10);
    s.push(20);
    while(!s.empty()){
        cout<<"栈顶元素为:"<<s.top()<<endl;
    }
    //出栈
    s.pop();
    cout<<"栈的大小为:"<<s.size()<<endl;
}
3.6 queue容器(队列)
3.6.1 queue基本概念

概念:queue是一种先进先出的数据结构。

队列中进数据称为-----入队 push

队列中出数据称为-----出队 pop

3.6.2 queue常用接口

在这里插入图片描述

#include<queue>
class Person {
public:
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};
void test01() {
	queue<Person> q;

	Person p1("刘1", 18);
	Person p2("刘2", 19);
	//入队
	q.push(p1);
	q.push(p2);
	while (!q.empty()) {
		cout << "姓名:" << q.front().m_Name << "年龄:" << q.front().m_Age << endl;
		q.pop();
	}
	cout << "队列大小:" << q.size() << endl;
}
3.7 list容器
3.7.1 list基本概念

功能:将数据进行链式存储。

链表list是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链实现的。

链表的组成:由一系列结点组成,结点由数据域(存储数据元素)和指针域(存储下一个结点的地址)组成。

由于链表的存储方式并不是连续的内存空间,因此不支持随机存取,list中的迭代器只支持前移和后移,属于双向迭代器。

优点:

  • 可以快速对任意位置进行插入删除元素,不需要移动大量元素。
  • 采样动态存储分配,不会造成内存浪费和溢出。

缺点:链表遍历速度没有数组快,占用空间比数组大。

List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器失效,而在vector是不成立的。

总结:STL中list和vector是两个最常用的容器,各有优缺点。

3.7.2 list构造函数

函数原型:(跟vector,deque一致)

在这里插入图片描述

//创建list容器
list<int> L1;//默认构造

//区间方式构造
list<int> L2(L1.begin(),L1.end());

//拷贝构造
list<int> L3(L2);

3.7.3 list赋值和交换

函数原型:(与vector一致)

在这里插入图片描述

list<int> L1;
L1.push_back(10);
L1.push_back(20);
list<int> L2;
//赋值
L2 = L1;
//交换
L2.swap(L1);

3.7.4 list大小操作

函数原型:

在这里插入图片描述

3.7.5 list插入和删除

在这里插入图片描述

3.7.6 list数据存取

函数原型:

  • front(); //返回第一个元素
  • back(); //返回最后一个元素

因为不是连续的空间,所以不支持随机存取,即不能用[ ]和at( )存取数据。

list<int> L1;
//验证迭代器是不支持随机访问的
list<int>::iterator it=L1.begin;
it++;
it--;//加减都可以,则支持双向访问
//it=it+1;//报错,不支持随机访问
3.7.7 list反转和排序

函数原型:

  • reverse(); //反转链表
  • sort(); //链表排序
#include<list>
void printList(const list<int> &L) {
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}
bool compare(int v1,int v2){
    return v1>v2;
}
void test01() {
	list<int> L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(50);
	L1.push_back(30);

	printList(L1);
	cout << "反转后:" << endl;
	L1.reverse();
	printList(L1);
	cout << "排序后:" << endl;
    //不支持随机访问迭代器的容器,内部会提供对应一些算法
    //sort(L1.begin(),L1.end());//这个全局算法不能在不能随机访问的容器使用
	L1.sort();//默认为升序
	printList(L1);
    //利用重载,提供一个函数变为降序
    L1.sort(compare);
}
3.7.8 排序案例

案例描述:将Person自定义数据类型进行排序

排序规则:按照年龄进行升序,如果年龄相同则按身高降序

class Person {
public:
	Person(string name,int age,int height) {
		this->m_Name = name;
		this->m_Age = age;
		this->m_Height = height;
	}
	string m_Name;
	int m_Age;
	int m_Height;
};

//指定排序规则
bool comparePerson(Person &p1,Person &p2) {
	if (p1.m_Age == p2.m_Age) {
		return p1.m_Height > p2.m_Height;
	}
	else {
		return p1.m_Age < p2.m_Age;
	}	
}
void test01() {
	list<Person> L;
	Person p1("刘备", 35, 175);
	Person p2("曹操", 45, 180);
	Person p3("孙权", 40, 170);
	Person p4("赵云", 35, 190);
	Person p5("关羽", 35, 185);
	//放入容器
	L.push_back(p1);
	L.push_back(p2);
	L.push_back(p3);
	L.push_back(p4);
	L.push_back(p5);

	L.sort(comparePerson);//提供一个函数指定算法如何排序
	for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名:" << it->m_Name << " 年龄:" << it->m_Age << " 身高:" << it->m_Height << endl;
	}
}
3.8 set/multiset 容器
3.8.1 set基本概念

简介:

  • 所有元素都会在插入时自动被排序

本质:

  • set/multiset属于关联式容器,底层结构是由二叉树实现的。

set和multiset区别:

  • set不允许容器中有重复的元素
  • multiset允许容器中有重复的元素
3.8.2 set构造和赋值

构造:

  • set<T> st; //默认构造
  • set(const set &st); //拷贝构造函数

赋值:

  • set& operator=(const set &st); //重载等号操作符
void printSet(set<int> s) {
	for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;

}
void test01() {
	set<int> s1;
	//插入数据,只有insert方式
	s1.insert(10);
	s1.insert(40);
	s1.insert(30);
	s1.insert(20);
	s1.insert(30);
	printSet(s1);//输出10,20,30,40,会默认排好序,并去除重复值
	//拷贝构造
	set<int> s2(s1);
	//赋值
	set<int> s3;
	s3 = s2;
}
3.8.3 set大小和交换

函数原型;

  • size(); //返回容器中元素的数目,记住它不支持resize()
  • empty();//判断容器是否为空
  • swap(st); //交换两个集合容器 // s1.swap(s2);
3.8.4 set插入和删除

在这里插入图片描述

3.8.5 set查找和统计

函数原型:

  • find(key); //查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end();
  • count(key); //统计key的元素个数
void test01(){
    set<int> s;
    s.insert(10);
    s.insert(30);
    s.insert(20);
    //查找,并返回位置的迭代器(地址)
    set<int>::iterator pos = s.find(30);
    if(pos!=s.end())P{
        cout<<"找到元素:"<<*pos<<endl;
    }
    else{
        cout<<"未找到元素"<<endl;
    }
    //统计
    int sum = s.count(30);
}
3.8.6 set和multiset区别
  • set不可以插入重复数据,而multiset可以
  • set插入数据的同时会返回插入结果,表示插入是否成功
  • multiset不会检测数据,因此可以插入重复数据
void test01() {
	set<int> s;
	pair<set<int>::iterator, bool> ret = s.insert(10);//pair<a,b>称为对组数据类型 
	if (ret.second) {
		cout << "第一次插入成功!" << endl;
	}
	else {
		cout << "第一次插入失败" << endl;
	}
    ret = s.insert(10);
	if (ret.second) {//第二次为false,所以插入失败
		cout << "第二次插入成功!" << endl;
	}
	else {
		cout << "第二次插入失败" << endl;//
	}
	multiset<int> ms;
	ms.insert(10);
	ms.insert(10);
	for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}
3.8.7 pair对组创建

成对出现的数据,可以利用对组来返回两个数据。

两种创建方式:

  • pair<type,type> p (value1,value2);
  • pair<type,type> p=make_pair(value1,value2);
void test01(){
    //第一种方式
    pair<string,int> p ("Tom",20);
    cout<<"姓名:"<<p.first<<"年龄"<<p.second<<endl;
    //第二种方式
    pair<string,int> p2=make_pair("Jerry",30);
    cout<<"姓名:"<<p2.first<<"年龄"<<p2.second<<endl;
}
3.8.8 set容器排序

学习目标:set容器默认排序规则为从大到小,掌握如何改变排序规则。

主要技术点:利用仿函数,可以改变排序规则

//利用仿函数
class Mycompare {
public:
	//加const后为常函数内,不可修改成员属性
	bool operator()(int v1,int v2) const {// 非常对象可以调用常函数,但是常对象只能调用常函数,不能调用其他任何函数。
		return v1 > v2;
	}
};
void test01() {
	
	//指定排序规则从大到小 按降序排列
	set<int,Mycompare> s1;//把数据类型Mycompare放入<>中,随后重载()符号
	s1.insert(20);
	s1.insert(10);
	s1.insert(50);
	s1.insert(40);
	s1.insert(30);

	for (set<int, Mycompare>::iterator it = s1.begin(); it != s1.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}
/**************************************/
//set容器存放自定义数据类型
class Person {
public:
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};
//指定排序类型,按年龄降序
class comparePerson {
public:
	bool operator()(const Person &p1,const Person &p2)const {
		return p1.m_Age > p2.m_Age;
	}
};
void test02() {
	//指定排序类型
	set<Person,comparePerson> s;
	Person p1("刘",24);
	Person p2("关", 27);
	Person p3("张", 25);
	Person p4("赵云", 21);
	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);
	
	for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++) {
		cout << "姓名:" << it->m_Name << " 年龄:" << it->m_Age << endl;
	}
	
}
3.9 map/multimap 容器
3.9.1 map基本概念

简介:

  • map中所有元素都是pair。
  • pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)。
  • 所有元素都会根据元素的键值自动排序

本质:

  • map/multimap属于关联式容器,底层结构是用二叉树实现的

优点:

  • 可以根据key值快速找到value值

map与multimap区别:

  • map不允许容器有重复key值元素
  • multimap允许容器中有重复key值元素
3.9.2 map构造和赋值

构造:

  • map<T1,T2> mp; //map默认构造
  • map<const map &mp>; //拷贝构造

赋值:

  • map& operator=(const map &mp)//重 载=符号
void printMap(map<int,int> &m){
    for(map<int,int>::iterator it=m.begin();it!=m.end();it++){
        cout<<"key="<<(*it).first<<"value="<<(*it).second<<endl;
    }
}

void test01(){
    //创建map容器
    map<int,int> m;
    m.insert(pair<int,int> (1,10));//将一个匿名对组放入容器中
    m.insert(pair<int,int> (2,20));
    m.insert(pair<int,int> (3,30));
    m.insert(pair<int,int> (4,40));
    printMap(m);//会根据key值自动排序 升序
    //拷贝构造
    map<int,int> m2(m);
    //=赋值
    map<int,int> m3;
    m3 = m2;
}
3.9.3 map大小和交换

函数原型:

  • size();//返回容器中元素的数目
  • empty(); //判断容器是否为空
  • swap(st);//交换两个容器集合
3.9.4 map插入和删除

函数原型:

  • insert(elem);//在容器中插入元素
  • clear(); //清除所有元素
  • erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
  • erase(beg,end); //删除区间[begin,end)的所有元素,返回下一个元素的迭代器
  • erase(key); //删除容器中值为key的元素
void test01(){
    map<int,int> m;
    //插入  第一种
    m.insert(pair<int,int> (1,10));
    //第二种
    m.insert(make_pair(2,20));
    //第三种
    m.insert(map<int,int>::value_type(3,30))
    //删除
    m.erase(m.begin());
    //按照key删除
    m.erase(3);//删除key=3的对组
    m.erase(m.begin(),m.end());
    m.clear();
}
3.9.5 map查找和统计

函数原型:

  • find(key);//查找key是否存在,若存在,返回该键元素的迭代器,若不存在,返回map.end()
  • count(key);//计算key的元素个数
map<int,int> m;
map<int,int>::iterator pos = m.find(3);
int num = m.count(3);//返回0或1,因为map不允许重复插入
3.9.6 map容器排序
//利用仿函数,改变排序规则
class myCompare {
public:
	bool operator()(int v1,int v2) const{
		//降序
		return v1 > v2;
	}
};

void test02() {
	map<int, int,myCompare> m;

	m.insert(make_pair(1, 40));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(2, 20));
	
	m.insert(make_pair(4, 20));
	m.insert(make_pair(5, 50));

	for (map<int, int,myCompare>::iterator it = m.begin(); it != m.end(); it++) {
		cout << "key值为:" << it->first << " value: " << it->second << endl;
	}
}
3.10 案例 员工分组
  1. 创建10名员工,放到vector中。

  2. 遍历vector容器,取出每个员工,进行随机分组

  3. 分组后,将员工部门编号作为key,具体员工工作为value,放入到multimap容器中

  4. 分部门显示员工信息。

#include<ctime>
#include<vector>
#include<map>
#define CEHUA 0
#define MEISHU 1
#define YANFA 2
class Worker {
public:
	string m_Name;
	int m_Salary;
};
void createWorker(vector<Worker> &w) {
	string nameSeed = "ABCDEFGHIJ";
	for (int i = 0; i < 10; i++) {
		Worker worker;
		worker.m_Name = "员工";
		worker.m_Name += nameSeed[i];
		worker.m_Salary = rand() % 10000 + 10000;
		w.push_back(worker);
	}
}

//员工分组
void setGroup(vector<Worker> &v,multimap<int,Worker> &w) {
	for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++) {
		//产生随机部门
		int deptId = rand() % 3;
		//key为部门编号,value为具体员工
		w.insert(make_pair(deptId, *it));
	}
}

void showWorkerByGroup(multimap<int,Worker> &m) {
	cout << "策划部门:" << endl;
	multimap<int, Worker>::iterator pos = m.find(CEHUA);
	int count = m.count(CEHUA);
	int index = 0;
	for (; pos != m.end() && index < count; index++, pos++) {
		cout << "姓名:" << pos->second.m_Name << " 工资:" << pos->second.m_Salary << endl;
	}
	cout << "******************************" << endl;
	cout << "美术部门:" << endl;
	pos = m.find(MEISHU);
	count = m.count(MEISHU);
	index = 0;
	for (; pos != m.end() && index < count; index++, pos++) {
		cout << "姓名:" << pos->second.m_Name << " 工资:" << pos->second.m_Salary << endl;
	}
	cout << "******************************" << endl;
	cout << "研发部门" << endl;
	pos = m.find(YANFA);
	count = m.count(YANFA);
	index = 0;
	for (; pos != m.end() && index < count; index++, pos++) {
		cout << "姓名:" << pos->second.m_Name << " 工资:" << pos->second.m_Salary << endl;
	}
}
int main() {
	//随机数种子
	srand((unsigned int)time(NULL));

	//1.创建员工
	vector<Worker> vWorker;
	createWorker(vWorker);

	//2.员工分组
	multimap<int, Worker> mWorker;
	setGroup(vWorker, mWorker);

	//3.分组显示员工
	showWorkerByGroup(mWorker);

	system("pause");
	return 0;
}

4 STL-函数对象

4.1 函数对象
4.1.1 函数对象概念

概念:

  • 重载函数调用操作符的类,其对象常称为函数对象
  • 函数对象使用重载的()时,行为类似函数调用,也叫仿函数

本质:

函数对象(仿函数)是一个类,不是一个函数。

4.1.2 函数对象使用

特点:

  • 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值。
  • 函数对象超出普通函数的概念,函数对象可以有自己的状态。
  • 函数对象可以作为参数传递。
//1.函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
class myAdd {
public:
	int operator()(int v1,int v2) {
		return v1 + v2;
	}
};

//2.函数对象超出普通函数的概念,函数对象可以有自己的状态。
class myPrint {
public:
	myPrint() {
		this->count = 0;
	}
	void operator()(string test) {
		cout << test << endl;
		count++;
	}
	int count;//内部自己的状态
};
void test01() {
	myAdd myadd;
	cout << myadd(10, 20) << endl;
}
void test02() {
	myPrint myprint;
	myprint("hello");
	myprint("hello");
	myprint("hello");
	myprint("hello");
	cout << myprint.count << endl;
}
//3.函数对象可以作为参数传递。
void doPrint(myPrint& mp,string test) {
	mp(test);
}
void test03() {
	myPrint myprint;
	doPrint(myprint, "hello");
}
4.2 谓词
4.2.1 谓词概念

概念:

  • 返回bool类型的仿函数称为谓词
  • 如果operator()接受一个参数,那么叫一元谓词
  • 如果operator()接受两个参数,那么叫二元谓词
4.2.2 一元谓词
#include<iostream>
using namespace std;
#include<string>
#include<vector>
#include<algorithm>
//一元谓词
class GreaterFive {
public:
	bool operator()(int val) {
		return val > 5;
	}
};
void test01() {
	vector<int> v;
	for (int i; i < 10; i++) {
		v.push_back(i);
	}
	//查找容器中有没有大于5的数,并返回迭代器
	//GreaterFive()表示匿名函数对象,也是一元谓词
	vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());//找到大于五的起始位置
	if (it==v.end()) {
		cout << "没有找到" << endl;
	}
	else {
		cout << *it << endl;//输出6
	}
}
4.2.3 二元谓词
//二元谓词
class MyCompare {
public:
	bool operator()(int val1,int val2) {
		return val1 > val2;
	}
};
void test01() {
	vector<int> v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(20);
	v.push_back(40);

	sort(v.begin(), v.end());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
	//使用函数对象,改变算法策略,变为降序
	cout << "变为降序后:" << endl;
	sort(v.begin(), v.end(), MyCompare());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}
4.3 内建函数对象
4.3.1 内建函数对象意义

概念:

  • STL内建了一些函数对象

分类:

  • 算术仿函数
  • 关系仿函数
  • 逻辑仿函数

用法:

  • 这些仿函数所产生的对象,用法和一般函数完全相同
  • 使用内建函数对象,需要引入头文件 #include<functional>
4.3.2 算术仿函数

功能描述:

  • 实现四则运算
  • 其中negate是一元运算,其他都是二元
    在这里插入图片描述
#include<functional>
//negate 取反仿函数
void test01(){
    negate<int> n;
    cout<<n(50)<<endl;//-50
}
//plus 加法仿函数
void test02(){
    plus<int> p;
    cout<<p(10,20)<<endl;//30
}
4.3.3 关系仿函数

函数原型:

在这里插入图片描述

#include<functioinal>
#include<vector>
void test(){
    vector<int> v;
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    //greater<int>()  内建函数对象
    sort(v.begin(),v.end(),greater<int>());//降序排序
    for(vector<int>::iterator it=v.begin();it!=end();it++){
        cout<<*it<<" ";
    }
    cout<<endl;
}
4.3.4 逻辑仿函数

实现逻辑运算:

在这里插入图片描述

#include<algorithm>
#include<vector>
#include<functional>
void test01(){
    vector<bool> v;
    v.push_back(false);
    v.push_back(true);
    v.push_back(true);
    v.push_back(false);
    //利用逻辑非 将容器v搬运到容器v2,并执行取反操作
    vector<bool> v2;
    v2.resize(v.size());//指定v2空间大小
    transform(v.begin(),v.end(),v2.begin(),logical_not<bool>());
    
}

5 STL-常用算法

概述:
在这里插入图片描述

5.1 常用遍历算法
5.1.1 for_each

函数原型:

  • for_each(iterator beg,iterator end,_func);
//for_each
//1.利用普通函数实现遍历操作
void print01(int val) {
	cout << val <<" ";
}
//2.利用仿函数实现遍历操作
class print02 {
public:
	void operator()(int val) {
		cout << val << " ";
	}
};
void test01() {
	vector<int> v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i);
	}
	//遍历,最后一个参数中放入函数名
	for_each(v.begin(), v.end(), print01);
	cout << endl;
	//仿函数遍历最后一个参数必须传函数对象才行
	for_each(v.begin(), v.end(), print02());
}
5.1.2 transform

功能:搬运容器到另一个容器

函数原型:

  • transform(iterator beg1,iterator end1,iterator begin2,_func)
class Transform {//转换时并存的仿函数,这里做的操作是元素搬过来后都+100
public:
	int operator()(int v) {
		return v+100;
	}
};
class MyPrint {//打印仿函数
public:
	void operator()(int val) {
		cout << val << " ";
	}
};
void test01() {
	vector<int> v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i);
	}
	vector<int> vTarget;
	vTarget.resize(v.size());
	transform(v.begin(), v.end(), vTarget.begin(), Transform());
	for_each(vTarget.begin(), vTarget.end(), MyPrint());
}
5.2 常用查找算法

算法简介:

  • find //查找元素
  • find_if //按条件查找元素
  • adjacent_find //查找相邻重复元素
  • binary_search //二分查找法
  • count //统计元素个数
  • count_if //按条件统计元素个数
5.2.1 find

功能描述:

  • 查找指定元素,找到返回指定元素的迭代器,找不到返回结束迭代器。

函数原型:

  • find(ietrator beg,iterator end,value);
//内置数据类型的查找
void test01() {
	vector<int> v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	vector<int>::iterator it = find(v1.begin(), v1.end(), 5);
	if (it == v1.end()) {
		cout << "没有这个数" << endl;
	}
	else {
		cout << *it << endl;
	}
}
//自定义数据类型的查找
class Person {
public:
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	//重载=符号 让底层fand知道Person数据类型
	bool operator==(const Person&p) {
		if (this->m_Name==p.m_Name&&this->m_Age==p.m_Age) {
			return true;
		}
		else {
			return false;
		}
	}

	string m_Name;
	int m_Age;
};
void test02() {
	vector<Person> v;
	Person p1("a", 13);
	Person p2("b", 14);
	Person p3("c", 15);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);

	Person pp("b", 14);
	//查找有没有跟pp一样的人
	vector<Person>::iterator it = find(v.begin(), v.end(), pp);
	if (it == v.end()) {
		cout << "查找失败" << endl;
	}
	else {
		cout << "姓名" << it->m_Name << "年龄" << it->m_Age << endl;
	}
}
5.2.2 find_if

功能:

  • 按条件查找元素

函数原型:

  • find_if(iterator beg, iterator end, _pred)//最后一个参数为函数或谓词
//1.内置数据类型
class GreaterFive {
public:
	bool operator()(int val) {
		return val > 5;
	}
};
void test01() {
	vector<int> v;
	for (int i; i < 10; i++) {
		v.push_back(i);
	}
	//查找容器中有没有大于5的数,并返回迭代器
	//GreaterFive()表示匿名函数对象,也是一元谓词
	vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());//找到大于五的起始位置
	if (it==v.end()) {
		cout << "没有找到" << endl;
	}
	else {
		cout << *it << endl;//输出6
	}
}

//2.查找自定义数据类型
class Person {
public:
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	
	string m_Name;
	int m_Age;
};

class greater13 {
public:
	bool operator()(Person &p) {
		return p.m_Age > 13;
	}
};
void test02() {
	vector<Person> v;
	Person p1("a", 13);
	Person p2("b", 14);
	Person p3("c", 15);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	//使用find_if按条件查,年龄大于13的
	vector<Person>::iterator it=find_if(v.begin(),v.end(),greater13());
	if (it == v.end()) {//这里不需要重载==,因为谓词指定好了明确比较属性m_Age
	cout << "查找失败" << endl;
    }
	else {
	cout << "姓名" << it->m_Name << "年龄" << it->m_Age << endl;
	}

}
5.2.3 adjacent_find

功能:

  • 查找相邻且重复的元素,并返回第一个的迭代器

函数原型:

  • adjacent_find(iterator beg,iterator end);
void test(){
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(1);
    v.push_back(3);
    v.push_back(3);
    v.push_back(2);
    vector<int>::iterator pos=adjacent_find(v.begin(),v.end());
    if(pos==v.end()){
        cout<<"未找到重复元素"<<endl;
    }
    else{
        cout<<*pos<<endl;//相邻且重复的元素为3,所有输出为3
    }
}

5.2.4 binary_search

功能:

  • 二分查找,查找指定元素是否存在。返回真或假。(注:无序序列中不可用)

函数原型:

  • bool binary_search(iterator beg,iterator end,value)
void test01(){
    vector<int> v;
    for(int i=0;i<10;i++){
        v.push_back(i);
    }
    //v.push_back(2); 再插入2进去后,变为无序序列,结果未知
    bool ret = binary_search(v.begin(),v.end(),9);
    if(ret){
        cout<<"找到了"endl;
    }
}
5.2.5 count

功能:统计元素个数

函数原型:

  • count(iterator beg,iterator end,value);

总结:统计自定义数据类型时候,需要配合重载 operator==

//1.统计内置数据类型
void test01() {
	vector<int> v;
	v.push_back(10);
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(10);
	int num = count(v.begin(), v.end(), 10);
	cout << num << endl;
}
//2.统计自定义数据类型
class Person {
public:
	Person(string name,int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	bool operator==(const Person &p) {
		if (this->m_Age==p.m_Age) {
			return true;
		}
		else {
			return false;
		}
	}
	string m_Name;
	int m_Age;
};
void test02() {
	vector<Person> v;
	Person p1("a", 30);
	Person p2("b", 40);
	Person p3("c", 40);
	Person p4("d", 35);
	
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	//统计vector容器中与e相同年龄的人
	Person p("e", 40);
	int num = count(v.begin(), v.end(), p);
	cout << num << endl;
}
5.2.6 count_if

功能:按条件统计元素个数

函数原型:

  • count_if(iterator beg, iterator end, _pred)//_pred谓词

案例参照find_if的案例。

5.3 常用排序算法

常用算法:

  • sort //对容器内元素进行排序
  • random_shuffle //洗牌,指定范围内的元素随机调整次序
  • merge //容器元素合并,并存储到另一容器中
  • reverse //反转指定范围的元素
5.3.1 sort

功能:

  • 对容器内元素进行排序

函数原型:

  • sort(iterator beg, iterator end, _pred)// _pred为谓词(函数对象)或一个函数,不填的话默认升序
#include<functioinal>
#include<vector>
#include<algorithm>
void test(){
    vector<int> v;
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    
    //greater<int>()  内建函数对象
    sort(v.begin(),v.end(),greater<int>());//降序排序
    for(vector<int>::iterator it=v.begin();it!=end();it++){
        cout<<*it<<" ";
    }
    cout<<endl;
}
5.3.2 random_shuffle

功能:洗牌,指定范围内的元素随机调整次序

函数原型:

  • random_shuffle(iterator beg,ietrator end);

可以加上随机数种子,让每次随机打乱得不一样。

#include<ctime>
#include<algorithm>
srand((unsigned int)time(NULL));
5.3.3 merge

功能:容器元素合并,并存储到另一容器中,

注:原容器都必须是有序序列,且都是升序或降序。将两个有序序列融合到一起,最后依旧是有序序列。

函数原型:

  • merge(ieterator beg1,iterator end1,ieterator beg2,iterator end2,iterator dest);//dest是目标容器开始迭代器
#include<algorithm>
void Myprint(int val){
    cout<<val<<" ";
}
void test(){
    vector<int> v1;
    vector<int> v2;
    for(int i=0;i<10;i++){
        v1.push_back(i);
        v2.push_back(i+1);
    }
    //目标容器
    vector<int> v3;
    //提前给v3分配空间
    v3.resize(v1.size()+v2.size());
    merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
    
    for_each(v3.begin(),v3.end(),Myprint);
    cout<<endl;
}
5.3.4 reverse

功能:将容器中的元素进行反转

函数原型:

  • reverse(iterator beg,iterator end);
5.4 常用拷贝和替换算法

算法简介:

  • copy //容器内指定范围的元素拷贝到另一个容器。
  • replace //将容器内指定范围的旧元素修改为新元素
  • replace_if //容器内指定范围满足条件的元素替换为新元素
  • swap //互换两个容器的元素
5.4.1 copy

功能:容器内指定范围的元素拷贝到另一个容器。

函数原型:

  • copy(iterator beg,iterator end,iterator dest)
vector<int> v1;
vector<int> v2;
v2.resize(v1.size());
copy(v1.begin(),v1.end(),v2.begin());
5.4.2 replace

功能:将容器内指定范围的旧元素修改为新元素

函数原型:

  • replace(iterator beg,iterator end,oldvalue,newvalue);
5.4.3 replace_if

功能:容器内指定范围满足条件的元素替换为新元素

函数原型:

  • replace_if(iterator beg,iterator end,_pred,newvalue); //_pred谓词告诉判断条件
class greater30{
public:
    bool operator()(int val){
        return val>30;
    }
};
class Myprint{
public:
    void operator()(int val){
        cout<<val<<" ";
    }
};
void test01(){
    vector<int> v;
    v.push_back(20);
    v.push_back(50);
    v.push_back(30);
    v.push_back(40);
    //将大于30的替换成100
    replace(v.begin(),v.end(),greater30(),100);
    //打印
    for_each(v.begin(),v.end(),Myprint());
}
5.4.4 swap

功能:互换同种类型的两个容器的元素

函数原型:

  • swap(container c1,container c2);
#include<algorithm>
vector<int> v1;
vector<int> v2;
swap(v1,v2);
5.5 常用算术生成算法(#include < numeric >)
5.5.1 accumulate

功能:计算区间内,容器元素累计总和

  • accumulate(iterator beg, iterator end, value); //参数value为起始累加值
#include<numeric>
void test(){
    vextor<int> v;
    for(int i=0;i<100;i++){
        v.push_back(i);
    }
    
    int total = accumulate(v.begin(),v.end(),0)//这里的0为起始累加值
}
5.5.2 fill

功能:向容器中填充指定的元素

函数原型:

  • fill(iterator beg,iterator end,value);
vector<int> v;
v.resize(10);//全部为0填充
//后期重新填充
fill(v.begin(),v.end(),100);//输出10个100
5.6 常用集合算法
5.6.1 set_intersection

功能:求两个容器的交集。两个容器都必须为有序序列。

函数原型:

  • set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin())//v3为目标容器
#include<algorithm>
class Myprint{
public:
    void operator()(int val){
        cout<<val<<" ";
    }
};
void test01(){
    vector<int> v1;
    vector<int> v2;
    for(int i=0;i<10;i++){
        v1.push_back(i);
        v2.push_back(i+5);
    }
    vector<int> v3;
    //目标容器需要提前开辟空间,特殊情况为大容器包含小容器,所以取小容器size即可
    v3.resize(min(v1.size(),v2.size()));
    //获取交集,并返回交集中最后一个元素的迭代器
    vector<int>::iterator itEnd = set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
    //遍历,使用最后一个交集元素的迭代器作为结束迭代器。
    for_each(v3.begin(),itEnd,Myprint());
}
5.6.2 set_union

功能:求两个容器的并集,两个容器都必须为有序序列。

函数原型:

  • set_union(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin()) //v3为目标容器
//目标容器需要提前开辟空间,特殊情况为二者无交集
v3.resize(v1.size()+v2.size());
//获取交集,并返回并集中最后一个元素的迭代器
    vector<int>::iterator itEnd = set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
//遍历,使用最后一个交集元素的迭代器作为结束迭代器。
    for_each(v3.begin(),itEnd,Myprint());
5.6.3 set_difference

功能:求两个容器的差集(不是交集的部分),两个容器都必须为有序序列。

函数原型:

  • ``set_difference(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin())` //v3为目标容器
//目标容器需要提前开辟空间,特殊情况为二者无交集,差集为大的size()
    v3.resize(max(v1.size(),v2.size());
//获取交集,并返回并集中最后一个元素的迭代器
//v1和v2的差集为:
    vector<int>::iterator itEnd = set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
//遍历,使用最后一个交集元素的迭代器作为结束迭代器。    
    for_each(v3.begin(),itEnd,Myprint());
//v2和v1的差集为:
    vector<int>::iterator itEnd = set_intersection(v2.begin(),v2.end(),v1.begin(),v1.end(),v3.begin());
//遍历,使用最后一个交集元素的迭代器作为结束迭代器。    
    for_each(v3.begin(),itEnd,Myprint());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值