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

news2025/7/18 6:57:26

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

查找相邻重复元素

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

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

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

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

相关文章

使用token登录提交到github

首先从GitHub上clone自己的仓库 git clone https://github.com/ljx2/SunnyWeather.git 修改一些文件 然后 git add ./ git commit -m ‘first commit’ 然后 git push origin main 提示输入用户名密码来验证 输完之后又提示需要创建token来上传&#xff0c;由于安全&#x…

M41T62Q6F 一款具有报警功能的低功耗串行实时时钟(RTC)芯片

M41T62Q6F是一款带有 32.768 kHz 振荡器的低功耗串行实时时钟(RTC)。8 个寄存器用于提供 时钟/日历功能&#xff0c;配置为二进制编码的十进制&#xff08;BCD&#xff09;格式。另有 8 个寄存器提供报警&#xff08;闹 铃&#xff09;、32 KHz 输出、校准以及看门狗功能的状态…

《树莓派项目实战》第七节 使用声音传感器检测声音

目录 7.1 引脚介绍 7.2 工作原理 7.3 连接到树莓派 7.4 编写代码检测声音有无 在本节&#xff0c;我们将学习如何使用声音传感器检测声音&#xff0c;该项目设计到的材料有&#xff1a; 树莓派 * 1面包板 * 1杜邦线若干声音传感器 * 17.1 引脚介绍 声音传感器一共有3个引脚…

噪声系数与插入损耗

目录噪声系数定义Friis噪声级联公式无源器件的噪声系数插入损耗&#xff1f;S参数文件能用来仿真噪声系数吗在计算射频链路的级联噪声时&#xff0c;我们会输入每一级的噪声系数以及增益&#xff0c;即可计算出整个射频链路的噪声系数&#xff0c;用于系统评估。但是有同学问我…

尚医通 (二十五) --------- 医院上传接口

目录一、医院系统模拟接口1. 服务部署2. 操作说明3. 上传接口与回调接口① 业务术语② 安全控制③ 业务接口④ 平台接口⑤ 医院接口二、集成 MongoDB三、添加医院基础类四、上传医院五、参数签名六、图片 Base64 编码一、医院系统模拟接口 1. 服务部署 ① 找到资源文件夹下面…

ArcGIS pro导出地图删除右下角小字制作者服务许可

ArcGIS pro导出地图删除右下角小字制作者服务许可 ArcGIS pro具有强大的制图功能&#xff0c;提供了丰富的底图。 研究者在使用这些底图出图时&#xff0c;发现地图右下角有__制作者名单、服务许可、来源&#xff1f;__ &#xff08;我也不知道它叫什么&#xff09;如下图 就…

【深入浅出Spring6】第六期——手写Spring框架与Spring IoC注解式开发

一、手写Spring框架 $ 准备工作 创建新的模块 myspring我们采用边测试边写框架的方式&#xff0c;所以还需要我们提供待测试的类 一号嘉宾——User类&#xff1a;用于一般类型传值测试 package com.powernode.myspring.bean; /*** author Bonbons* version 1.0*/ public clas…

【面试题】CSS响应式

1. rem是什么&#xff1f; rem&#xff0c;CSS3新增的一个相对单位&#xff08;root em&#xff0c;根em&#xff09;&#xff0c;相对于根元素&#xff0c;常用于响应式布局em&#xff0c;相对长度单位&#xff0c;相对于父元素&#xff0c;不常用px&#xff0c;像素&#xf…

中兴通讯完成基于低频 5G 商用基站的通感融合测试验证

11 月 16 日消息&#xff0c;今年 11 月&#xff0c;在 IMT-2020&#xff08;5G&#xff09;推进组的指导下&#xff0c;中兴通讯使用 4.9GHz 低频 5G 商用基站&#xff0c;完成了室外无人机、车辆和行人感知测试验证以及室内场景下呼吸感知的测试验证。验证结果显示&#xff0…

api-ms-win-crt-runtime-l1-1-0.dll文件加载失败是怎么造成的?怎么修复?

电脑中是含有大量不同类型的软件程序的&#xff0c;这些软件程序为用户们提供了丰富的功能&#xff0c;用户们使用这些软件的时候虽然看上去比较简单&#xff0c;但是电脑系统内部却是会调用大量文件的&#xff0c;在软件执行命令的过程中无论哪个环节出现了问题&#xff0c;都…

DDD系列 实战一 应用设计案例 (golang)

DDD系列 实战一 应用设计案例 (golang) 基于 ddd 的设计思想, 核心领域需要由纯内存对象基础设施的抽象的接口组成 独立于外部框架: 比如 web 框架可以是 gin, 也可以是 beego独立于客户端: 比如客户端可以是 web, 可以是移动端, 也可以是其他服务 rpc 调用独立于基础组件: 比如…

数据结构计算二叉树的深度和节点个数

2022.11.19 计算二叉树的深度和节点个数任务描述相关知识编程要求测试说明C/C代码任务描述 本关任务&#xff1a;给定一棵二叉树&#xff0c;计算该二叉树的深度、总节点个数和叶子节点个数。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.二叉树深度概念…

【Java八股文总结】之类

文章目录Q&#xff1a;一个Java文件中可以有多个类么&#xff08;不含内部类&#xff09;&#xff1f;一、Object类1、Object类的常见方法有哪些&#xff1f;2、 和 equals() 的区别3、HashCode()的作用&#xff1f;4、为什么要有hashCode&#xff1f;5、为什么重写equals()时必…

古人的雅趣

学习古人雅趣&#xff0c;为今日生活增添情趣。 目录 曲水流觞 九月九日重阳节赏菊 中秋赏月 一、曲水流觞 中国古代汉族民间的一种传统习俗&#xff0c;后来发展成为文人墨客诗酒唱酬的一种雅事。 夏历的三月上巳日人们举行祓禊&#xff08;fx&#xff09;仪式之后&#xf…

单流 TCP 100Gbps+ 难题的直观解释

关于 400Gbps 场景的概括&#xff0c;参见&#xff1a;400Gbps 网络面临的挑战 首先定义规则&#xff1a; 画坐标系&#xff0c;横轴为到达时间&#xff0c;纵轴为服务(处理)时间。每到达一个任务在横轴对应时间画一条垂直线。每处理一个任务在纵轴对应时间画一条水平线。任务…

【Linux】文件操作/文件描述符/重定向原理/缓冲区

目录 一.文件的概念 1.什么是内存级文件? 2.什么是磁盘级文件? 3.文件IO的过程 4.linux下, 一切皆文件 二.文件操作(C语言接口) 1.语言级文件接口与系统级文件接口 1).什么是语言级别的文件接口? 2).为什么要有语言级别文件接口, 直接用系统接口不好吗? 3).系统级…

老系统如何重构之最全总结

目录 1. 重构的概念 1.1 重构的定义 1.2 重构的分类 2 为什么重构 3 如何重构 3.1 说服业务方 3.2 确定重构的目标 3.3 老系统的熟悉与梳理 3.4 数据库的重构 3.5 前后端的系统重构 3.6 数据迁移与检查 3.7 系统检查联调测试 3.8 系统切换 1. 重构的概念 1.1 重构…

DlhSoft Gantt Chart Hyper Library for HTML5 Standard Edition

DlhSoft Gantt Chart Hyper Library 甘特图超级库包括一组交互式计划组件&#xff0c;可用于使用纯 JavaScript、TypeScript 或 Angular、React 或 Vue 等框架构建的启用时间线的应用程序 基于 JavaScript 的甘特图 可定制的网格列、汇总的工作分解结构、带有可拖动条和依赖线…

静态HTML网页设计作品 DIV布局家乡介绍网页模板代码---(太原 10页带本地存储登录注册 js表单校验)

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 家乡旅游景点 | 家乡民生变化 | 介绍自己的家乡 | 我的家乡 | 家乡主题 | HTML期末大学生网页设计作业 HTML&#xff1a;结构 CSS&#xff1a;样式 在…

基于ssm jsp超市在线销售平台的设计与实现

近年来&#xff0c;网络信息技术的迅猛发展&#xff0c;互联网逐渐渗透到人们日常生活中的方 方面面&#xff0c;而给我们的生活带来巨大变化的电子商务正在以前所未有的速度蓬勃发 展&#xff0c;电子商务也成为网络研究与应用的热点之一。网上商店是电子商务的重要方 面&…