C++学习笔记(Ⅳ):C++提高编程

目录

1 模板

1.1 模板的概念

1.2 函数模板

1. 语法

2.注意事项

3.模板案例

4.普通函数与函数模板区别

5.普通函数与函数模板的调用规则

6.模板的局限性

1.3 类模板

1.语法

2.类模板与函数模板的区别

3.类模板中成员函数创建时机

4.类模板对象做函数参数

5.类模板与继承

6.类模板成员函数类外实现

7.类模板分文件编写

8.类模板与友元

9.类模板案例

2 STL初始

2.1 STL基本概念

2.2 STL组件

2.3 STL容器、算法及迭代器

2.4 容器算法、迭代器初识

1.vector存放内置数据类型

2.vector存放自定义数据类型

3.vector容器嵌套

3 STL常用容器

3.1 string容器

1.string概念

2.string构造函数

3.string赋值操作

4.string字符串拼接

5.string查找和替换

6.string字符串比较

7.string存取

8.string插入和删除

9.string子串

3.2 vector容器

1.vector概念

2.vector构造函数

3.vector赋值操作

4.vector容量和大小

5.vector插入和删除

6.vector数据存取

7.vector互换容器

8.vector预留空间

3.3 deque容器

1.deque基本概念

2.deque构造函数

3.deque赋值操作

4.deque大小操作

5.deque插入和删除

6.deque数据存取

7.deque排序

        3.4 案例-评委打分

3.5 stack容器

1.概念

2.常用接口

3.6 queue容器

1.queue概念

2.queue常用接口

3.7 list容器

1.list概念

2.list构造函数

3.list赋值和交换

4.list大小操作

5.list插入和删除

6.list数据存取

7.list反转和排序

8.排序案例

3.8 set/multiset容器

1.set基本概念

2.set构造和赋值

3.set大小和交换

4.set插入和删除

5.set查找和统计

6.set和multiset区别

7.pair对组创建

8.set容器排序

3.9 map/multimap容器

1.map概念

2.map构造和赋值

3.map大小和交换

4.map插入和删除

5.map查找和统计

6.map容器排序

3.10 案例-员工分组

4 STL函数对象

4.1 函数对象

1.函数对象概念

2.函数对象使用

4.2 谓词

1.概念

2.一元谓词

3.二元谓词

4.3 内建函数对象

1.内建函数对象

2.算术仿函数

3.关系仿函数

4.逻辑仿函数

5 STL常用算法

5.1 常用遍历算法

1.for_each

2.transform

5.2 常用查找算法

1.find

2.find_if

3.adjacent_find

4.binary_search

5.count

6.count_if

5.3 常用排序算法

1.sort

2.random_shuffle

3.merge

4.reverse

5.4 常用拷贝和替换算法

1.copy

2.replace

3.replace_if

4.swap

5.5 常用算术生成算法

1.accumulate

2.fill

5.6 常用集合算法

1.set_intersection

2.set_union

3.set_difference


1 模板

1.1 模板的概念

建立通用的模板,提高代码复用性

1.2 函数模板

·c++还有一种利用模板的泛型编程

1. 语法

建立函数,其返回值类型和形参类型用虚拟类型代表

template<typename T>

// 函数模板
template<typename T>    //  声明一个模板,T是通用数据类型
void mySwap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

void test()
{
	int a = 10;
	int b = 20;
	// 1.自动类型推导
	mySwap(a, b);
	mySwap<int>(a, b);
	// 2.显示指定类型
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
}

将类型参数化

2.注意事项

·自动类型推导:必须推导出一致的数据类型T

·模板必须确定出T的数据类型才可以使用

3.模板案例

// 交换函数模板
template<class T>
void myswap(T &a, T &b)
{
	int temp = a;
	a = b;
	b = temp;
}
// 排序算法
template<typename T>    //  声明一个模板,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]);
		}
	}
}

void test()
{
	char chararr[] = "cabdjukadn";
	int num = sizeof(chararr) / sizeof(char);
	mySort(chararr, num);
	for (int i = 0; i < num; i++)
	{
		cout << chararr[i];
	}
	cout << endl;
}

4.普通函数与函数模板区别

·普通函数调用时可以发生隐式类型转换

·函数模板调用时,若利用自动类型推导,不会发生隐式类型转换

·若利用显示指定类型的方式,可以发生隐式类型转换(建议使用)

5.普通函数与函数模板的调用规则

·若函数模板和普通函数都可以实现,优先调用普通函数

·可通过空模板参数列表来强制调用函数模板

·函数模板可以发生重载

·若函数模板可以产生更好的匹配,优先调用函数模板

提供函数模板后不要提供普通函数,避免产生二义性

6.模板的局限性

模板不是万能的。无法实现自定义数据类型的通用化,需要利用模板重载具体化数据类型

// myCompare 为模板,进行重载
template<> bool myCompare(Person &a,Person &b)

1.3 类模板

1.语法

与函数模板大体一致

// 类模板
template<class Nametype,class Agetype>
class Person
{
public:
	Person(Nametype name, Agetype age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	Nametype m_name;
	Agetype m_age;
};

2.类模板与函数模板的区别

·类模板中没有自动类型推导的方式

·类模板在模板参数列表中可以有默认参数

3.类模板中成员函数创建时机

·普通成员函数一开始创建

·类模板中成员函数在调用时创建

4.类模板对象做函数参数

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

·指定传入的类型:直接显示对象的数据类型

// 类模板对象做函数参数
template<class Nametype, class Agetype>
class Person
{
public:
	Person(Nametype name, Agetype age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	void showPerson()
	{
		cout << "name:" << this->m_name << endl;
		cout << "age:" << this->m_age << endl;
	}
	Nametype m_name;
	Agetype m_age;
};
// 1.指定传入类型
void printPerson1(Person<string, int>&p)
{
	p.showPerson();
}
void test01()
{
	Person<string, int>p("sun", 100);
	printPerson1(p);
}

·参数模板化:将对象中的参数变为模板进行传递

// 2.参数模板化
template<class Nametype,class Agetype>
void printPerson2(Person<Nametype, Agetype>&p)
{
	p.showPerson();
}
void test02()
{
	Person<string, int>p("zhu", 99);
	printPerson2(p);
}

·整个类模板化:将这个对象类型模板化进行传递

// 3.整个类模板化
template<class T>
void printPerson3(T &p)
{
	p.showPerson();
}
void test03()
{
	Person<string, int>p("sha", 90);
	printPerson3(p);
}

5.类模板与继承

·子类继承的父类为类模板时,子类在声明的时候,要指定父类T的类型,否则无法给子类分配内存

// 类模板与继承
template<class T>
class Base
{
	T m;
};
class son:public Base<int>
{

};

·若需灵活指定父类T类型,子类需变为类模板

// 类模板与继承
template<class T>
class Base
{
	T m;
};
template<class T1,class T2>
class son:public Base<T2>
{
	T1 obj;
};

6.类模板成员函数类外实现

类外实现时,类内只写函数声明

// 类模板成员函数类外实现
template<class T1,class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	void show();

	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>::show()
{
	cout << "姓名:" << this->m_name << "年龄:" << this->m_age << endl;
}

7.类模板分文件编写

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

void test()
{
	Person<string, int>P("time", 20);
	P.show();
}

类的声明在头文件中,类的实现在源文件中。此时主函数中调用上述代码会报错。

解决方法1:直接包含.cpp源文件

#include<iostream>
using namespace std;
#include<string>
#include"Person.cpp"

解决方法2:将声明和实现写到同一个文件中,更改后缀名为.hpp

#include"Person.hpp"

8.类模板与友元

全局函数类内实现:在类内声明友元

全局函数类外实现:提前让编译器得知全局函数的存在

// 让编译器提前获取类外函数
template<class T1, class T2>
class Person;

template<class T1, class T2>
void PrintPerson(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);
public:
	Person(T1 name, T2 age)
	{
		this->m_name = name;
		this->m_age = age;
	}
private:
	T1 m_name;
	T2 m_age;
};

9.类模板案例

在.hpp文件中编写类模板

#pragma once
#include<iostream>
using namespace std;
#include<string>
// 通用数组类
template<class T>
class myArray
{
public:
	// 有参构造 参数 容量
	myArray(int capacity)
	{
		//cout << "有参构造调用" << endl;
		// 属性初始化
		this->m_Capacity = capacity;
		this->m_Size = 0;
		this->pAddress = new T[this->m_Capacity];    // 按照容量大小开辟堆区空间
	}
	// 拷贝构造(防止浅拷贝)
	myArray(const myArray& arr)
	{
		//cout << "拷贝构造调用" << endl;
		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];
		}
	}
	myArray& operator=(const myArray& arr)
	{
		//cout << "operator=调用" << endl;
		// 先判断堆区是否有数据,若有先释放
		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];
		}
	}
	// 尾插法
	void pushBack(const T & val)
	{
		// 判断容量是否等于大小
		if (this->m_Capacity == this->m_Size)
		{
			return;
		}
		this->pAddress[this->m_Size] = val;   // 在数组末尾插入数据
		this->m_Size++;    // 更新数组大小
	}
	// 尾删法
	void popBack()
	{
		// 让用户无法访问末尾
		if (this->m_Size==0)
		{
			return;
		}
		this->m_Size--;
	}
	// 通过下标访问数组元素
	T& operator[](int index)       // operator重载运算符[]
	{
		return this->pAddress[index];
	}
	// 返回数组容量
	int getCapacity()
	{
		return this->m_Capacity;
	}
	// 返回数组大小
	int getSize()
	{
		return this->m_Size;
	}
	
	// 析构函数
	// 释放堆区空间
	~myArray()
	{
		//cout << "析构函数调用" << endl;
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;           // 置空防止野指针
		}
	}
private:
	T * pAddress;       // 指针指向堆区开辟的真实数组
	int m_Capacity;     // 数组容量
	int m_Size;         // 数组大小
};

其中:构造函数为初始化、拷贝构造防止浅拷贝、operator=重载操作符防止浅拷贝、析构函数清空堆区内容。

源文件

#include<iostream>
using namespace std;
#include<string>
#include"myArray.hpp"
void printIntArray(myArray<int>&arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << arr[i] << endl;
	}
}
void test()
{
	myArray<int>arr1(5);
	for (int i = 0; i < 5; i++)
	{
		arr1.pushBack(i);
	}
	printIntArray(arr1);
	cout << "arr1的容量" << arr1.getCapacity() << endl;
	cout << "arr1的大小" << arr1.getSize() << endl;
	
	myArray<int>arr2(arr1);
	printIntArray(arr2);
	arr2.popBack();
	cout << "arr2尾删后的容量" << arr2.getCapacity() << endl;
	cout << "arr12尾删后的大小" << arr2.getSize() << endl;
}

int main()
{
	test();
	system("pause");
	return 0;
}

2 STL初始

2.1 STL基本概念

1.概念:STL(standard template library,标准模板库)

2.STL从广义上分为:容器、算法、迭代器

3.容器和算法间通过迭代器连接

2.2 STL组件

1.容器:各类数据结构

2.算法:各类常用算法

3.迭代器:容器与算法间的桥梁

4.仿函数:行为类似函数

5.适配器:修饰容器或仿函数或迭代器接口

6.空间配置器:空间配置与管理

2.3 STL容器、算法及迭代器

1.容器分为序列式容器(强调值的排序,序列式容器中每个元素均有固定位置)与关联式容器(二叉树结构,各元素间没有严格物理上的顺序关系)

2.算法分为质变算法(运算期间会更改区间元素内容)和非质变算法(运算期间不会更改区间元素内容)

3.迭代器种类

2.4 容器算法、迭代器初识

1.vector存放内置数据类型

#include<iostream>
using namespace std;
#include<string>
#include<vector>
#include<algorithm>
// vector容器存放内置数据类型
void myPrint(int val)
{
	cout << val << endl;
}
void test()
{
	// 创建vector容器,可认为是数组
	vector<int> v;
	// 向容器中插入数据
	v.push_back(10);
	v.push_back(1200);
	v.push_back(120);
	
	// 第一种遍历方式
	// 通过迭代器访问容器中的数据
	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(), myPrint);
}

2.vector存放自定义数据类型

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

void test()
{
	// 创建vector容器
	vector<Person> v;
	Person p1("a", 10);
	Person p2("b", 20);
	Person p3("c", 30);

	// 向容器中添加数据
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	
	// 通过迭代器访问容器中的数据
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" <<(*it).name<< " 年龄:"<<(*it).age << endl;
		// 以下相同
		//cout << "姓名:" << it->name<< " 年龄:" << it->age << endl;
	}
}
// 存放自定义数据类型的指针
void test01()
{
	// 创建vector容器
	vector<Person*> v;
	Person p1("a", 10);
	Person p2("b", 20);
	Person p3("c", 30);

	// 向容器中添加数据
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);

	// 遍历
	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it)->name << " 年龄:" << (*it)->age << endl;
	}
}

3.vector容器嵌套

// vector容器嵌套
void test()
{
	// 创建vector容器
	vector<vector<int>> v;
	// 创建内层容器
	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	for (int i = 0; i < 4; i++)
	{
		v1.push_back(i + 1);
		v2.push_back(i + 2);
		v3.push_back(i + 3);
	}
	// 创建外层容器
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	// 通过迭代器访问容器中的数据
	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)
	{
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
		{
			cout << *vit;
		}
		cout << endl;

	}
}

3 STL常用容器

3.1 string容器

1.string概念

string本质上是个类,是一个char*型的容器。类内提供了多种成员方法

2.string构造函数

string();   默认构造

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

string(const string str);   使用一个string对象初始化另一个string对象

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

3.string赋值操作

 常用前三种方式赋值

    // 方法1
	string str1;
	str1 = "hello";

	// 方法2
	string str2;
	str2 = str1;

	// 方法3
	string str3;
	str3 = 'a';

	// 方法4
	string str4;
	str4.assign("hello");

	// 方法5
	string str5;
	str5.assign("hello", 4);

	// 方法6
	string str6;
	str6.assign(str5);

	// 方法7
	string str7;
	str7.assign(10, '7');

4.string字符串拼接

	// 方法1
	string str1 = "hello";
	str1 += "world";

	// 方法2
	string str2 = "!";
	str1 += str2;

	// 方法3
	string str3 = "hello";
	str3.append("world");

	// 方法4
	string str4 = "hello";
	str4.append("hello",4);

	// 方法5
	string str5 = "hello";
	str5.append(str2);

	// 方法6
	string str6;
	str6.append(str5,0,3);

5.string查找和替换

	// 查找
	// 方法1
	string str1 = "hello";
	str1.find("l");   // 若存在返回1,否则返回-1

	// 方法2
	str1.rfind("l");   // rfind从右往左查,find查找顺序相反

	// 替换
	str1.replace(1,3,"11");

6.string字符串比较

字符串以ASCii大小进行比较,相等返回0,大于返回1,小于返回-1

	// 字符串比较
	string str1 = "hello";
	string str2 = "hello";
	if (str1.compare(str2) == 0)
	{
		cout << "相等" << endl;
	}

7.string存取

	// 字符串存取
	string str = "hello world";
	// 方法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;
	}

8.string插入和删除

	// 字符串插入与删除
	string str = "hello world";
	
	str.insert(1, "11");   // 插入

	str.erase(1, 2);      // 删除

9.string子串

	// 字符串子串获取
	string str = "hello world";
	
	string subStr = str.substr(1, 3);

3.2 vector容器

1.vector概念

vector与数组类似,称为单端数组;不同于数组是静态空间,vector可以动态扩展(寻找更大的内存空间,将源数据拷贝至新空间,并释放原空间)

vector容器的迭代器是支持随机访问的迭代器

2.vector构造函数

创建vector容器

	vector<int> v1;     // 无参构造

	vector<int> v2(v1.begin(), v1.end());   // 通过区间方式进行构造

	vector<int> v3(10, 100);    // n个elem方式构造

	vector<int> v4(v3);         // 拷贝构造

3.vector赋值操作

	// 无参构造
	vector<int> v1;     
    // operator=
	vector<int> v2;
	v2 = v1;         
	// assign
	vector<int> v3;        
	v3.assign(v1.begin(), v1.end());   
	// n个elem赋值
	vector<int> v4;  
	v4.assign(10, 100);

4.vector容量和大小

5.vector插入和删除

6.vector数据存取

7.vector互换容器

swap可以收缩空间

	vector<int> v;  
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	cout << v.capacity() << endl;
	cout << v.size() << endl;
	vector<int>(v).swap(v);
	cout << v.capacity() << endl;
	cout << v.size() << endl;

8.vector预留空间

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

3.3 deque容器

1.deque基本概念

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

2.deque构造函数

3.deque赋值操作

4.deque大小操作

5.deque插入和删除

6.deque数据存取

7.deque排序

需包含算法头文件

对于支持随机访问的迭代器的容器(vector),都可利用sort算法直接对其排序。

3.4 案例-评委打分

// 选手类
class Person
{
public:
	Person(string name, int score)
	{
		this->m_name = name;
		this->m_score = score;
	};
	string m_name;
	int m_score;
};
// 创建选手
void creatPerson(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);
		// 将创建的person对象,放入到容器中 
		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;
			d.push_back(score);
		}
		// 排序
		sort(d.begin(), d.end());
		// 去除最高与最低分
		d.pop_back();
		d.pop_front();
		// 取平均分
		int sum = 0;
		for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
		{
			sum += *dit;
		}
		int avg = sum / d.size();
		// 赋值给选手
		it->m_score = avg;
	}
}
// 显示得分
void showScore(vector<Person> &v)
{
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << it->m_name << " 平均分:"<< it->m_score << endl;
	}
}

3.5 stack容器

1.概念

stack是一种先进后出的数据结构,只有一个出口。栈不允许有遍历行为

2.常用接口

3.6 queue容器

1.queue概念

queue是一种先进先出的数据接口,只有队头和队尾才可被访问,不可遍历。

2.queue常用接口

 

3.7 list容器

1.list概念

·将数据进行链式存储

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

·链表由一系列结点组成,结点一个是存储数据元素的数据域,另一个是村查下一个结点地址都指针域

·STL中的链表是一个双向循环链表

·可以对任意位置进行快速插入或删除元素,但遍历速度没有数组快,且占用空间比数组大

 链表中的迭代器只支持前移和后移,属于双向迭代器

2.list构造函数

3.list赋值和交换

4.list大小操作

5.list插入和删除

6.list数据存取

list不能用[]与at方式访问元素,迭代器不支持随机访问

7.list反转和排序

8.排序案例

// 类定义
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;
	}
	return p1.m_age < p2.m_age;
}
void test()
{
	list<Person> L;
	// 准备数据
	Person p1("刘备", 35, 175);
	Person p2("曹操", 45, 180);
	Person p3("孙权", 40, 170);
	Person p4("张飞", 25, 190);
	Person p5("关羽", 35, 160);
	Person p6("赵云", 35, 200);
	// 插入数据
	L.push_back(p1);
	L.push_back(p2);
	L.push_back(p3);
	L.push_back(p4);
	L.push_back(p5);
	L.push_back(p6);
	for (list<Person>::iterator it = L.begin(); it != L.end(); it++)
	{
		cout << "姓名:" << (*it).m_name << " 年龄:" << (*it).m_age << " 身高:" << (*it).m_height<< endl;
	}
	// 排序
	cout << "排序后:" << endl;
	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容器

1.set基本概念

·所有元素在插入时被自动排序

·属于关联式容器,底层结构用二叉树实现

·不允许有重复元素

2.set构造和赋值

3.set大小和交换

4.set插入和删除

5.set查找和统计

6.set和multiset区别

7.pair对组创建

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

// 对组的创建
	pair<string, int>p1("tom", 20);
	cout << p1.first << p1.second << endl;
	pair<string, int>p2 = make_pair("jerry", 30);
	cout << p2.first << p2.second << endl;

8.set容器排序

可以利用仿函数改变排序规则

·内置数据类型排序

// 仿函数
class mycompare
{
public:
	bool operator()(int v1,int v2)   // 第一个()代表重载符,第二个()代表函数参数列表
	{
		return v1 > v2;
	}
};
void test()
{
	set<int, mycompare>s1;
	s1.insert(10);
	s1.insert(20);
	s1.insert(40);
	s1.insert(30);
	for (set<int>::iterator it = s1.begin(); it != s1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

·自定义数据类型排序

class Person
{
public:
	Person(string name, int age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	string m_name;
	int m_age;
};
class mycompare
{
public:
	bool operator()(const Person &p1, const Person &p2)   // 第一个()代表重载符,第二个()代表函数参数列表
	{
		return p1.m_age > p2.m_age;
	}
};
void test()
{
	// 自定义数据类型要指定排序规则
	set<Person, mycompare>s;
	Person p1("tom", 24);
	Person p2("jarry", 34);
	Person p3("luffy", 25);
	Person p4("zoro", 14);
	Person p5("sam", 55);

	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);
	s.insert(p5);

	for (set<Person, mycompare>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << it->m_name <<  " " << it->m_age <<endl;
	}
}

3.9 map/multimap容器

1.map概念

·map中所有元素都是pair

·pair中首个元素为key(键值),起到索引作用,第二个元素为value(实值)

·所有元素都会根据元素的键值自动排序

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

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

·map中不允许有重复key值元素

2.map构造和赋值

 

数据插入时要使用对组

3.map大小和交换

4.map插入和删除

5.map查找和统计

6.map容器排序

利用仿函数可以改变排序规则

3.10 案例-员工分组

// 创建员工类
class Worker
{
public:
	string m_name;
	int m_salary;
};
void creatWorker(vector<Worker> &v)
{
	string workerName = "ABCDEFGHIJ";
	for (int i = 0; i < 10; i++)
	{
		Worker worker;
		worker.m_name = workerName[i];
		worker.m_salary = rand()%10000 + 10000;   // 10000--19999
		v.push_back(worker);
	}
}
// 员工分组
void setGroup(vector<Worker> &v, multimap<int, Worker> &m)
{
	for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++)
	{
		// 产生随机部门编号
		int deptId = rand() % 3;   // 0 1 2
		m.insert(make_pair(deptId, *it));
	}
}
void showWorker(multimap<int, Worker> &m)
{
	cout << "0号部门成员:" << endl;
	multimap<int, Worker>::iterator pos = m.find(0);
	int count = m.count(0);
	int index = 0;
	for (; pos != m.end() && index < count; pos++,index++)
	{
		cout << "姓名:" << pos->second.m_name << " 工资:" << pos->second.m_salary << endl;
	}

	cout << "1号部门成员:" << endl;
	pos = m.find(1);
	count = m.count(1);
	index = 0;
	for (; pos != m.end() && index < count; pos++, index++)
	{
		cout << "姓名:" << pos->second.m_name << " 工资:" << pos->second.m_salary << endl;
	}

	cout << "2号部门成员:" << endl;
	pos = m.find(2);
	count = m.count(2);
	index = 0;
	for (; pos != m.end() && index < count; pos++, index++)
	{
		cout << "姓名:" << pos->second.m_name << " 工资:" << pos->second.m_salary << endl;
	}
}

4 STL函数对象

4.1 函数对象

1.函数对象概念

本质上是一个类 

2.函数对象使用

// 仿函数
// 可以像普通函数一样调用,可以有参数值与返回值
class myAdd
{
public:
	int operator()(int v1, int v2)
	{
		return v1 + v2;
	}
};
void test01()
{
	myAdd a;
	cout << a(10, 10) << endl;
}
// 函数对象可以有自己的状态
class myPrint
{
public:
	myPrint()
	{
		this->count = 0;
	}
	void operator()(string test)
	{
		cout << test << endl;
		this->count++;
	}
	int count;   // 记录内部状态

};
// 函数对象可以作为参数传递

4.2 谓词

1.概念

2.一元谓词

// 一元谓词
// 仿函数
class GreaterFive
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}
};
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(), GreaterFive());

3.二元谓词

// 二元谓词
// 仿函数
class mycompare
{
public:
	bool operator()(int v1,int v2)
	{
		return v1 > v2;
	}
};
void test()
{
	vector<int> v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(20);
	v.push_back(40);
	v.push_back(50);

	sort(v.begin(), v.end(), mycompare());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << endl;
	}
}

4.3 内建函数对象

1.内建函数对象

需要添加头文件functional

2.算术仿函数

实现四则运算

	// 一元仿函数
	negate<int> n;
	cout << n(50) << endl;
	// 二元仿函数
	plus<int> p;
	cout << p(10, 20) << endl;

3.关系仿函数

4.逻辑仿函数

5 STL常用算法

5.1 常用遍历算法

·算法头文件:algorithm、functional、numeric

1.for_each

2.transform

搬运容器到另一个容器中,搬运前需要给目标容器开辟空间

5.2 常用查找算法

1.find

按值查找元素

若查找自定义数据类型,需在类中重载==运算符

2.find_if

按条件查找元素

3.adjacent_find

查找相邻重复元素

查找指定元素是否存在

5.count

统计元素个数

6.count_if

按条件统计元素个数

5.3 常用排序算法

1.sort

2.random_shuffle

3.merge

4.reverse

5.4 常用拷贝和替换算法

1.copy

2.replace

3.replace_if

4.swap

5.5 常用算术生成算法

1.accumulate

2.fill

5.6 常用集合算法

1.set_intersection

2.set_union

3.set_difference

 目标容器开辟空间需要从两个容器取较大值

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值