【C++】【第六篇-2】【黑马 p215~p242】C++学习记录之C++提高编程

【C++】【第六篇-2】【黑马 p215~p242】C++学习记录之C++提高编程,第1张

【C++】【第六篇-2】【黑马 p215~p242】C++学习记录之C++提高编程
    • 3.7 list容器(p215)
      • 3.7.1 list基本概念
      • 3.7.2 list构造函数
      • 3.7.3 list赋值和交换(p217)
      • 3.7.4 list大小 *** 作(p218)
      • 3.7.5 list插入和删除(p219)
      • 3.7.6 list数据存取(p220)
      • 3.7.7 list反转和排序(p221)
      • 3.7.8 排序案例(p222)
    • 3.8 set/multiset 容器(p223)
      • 3.8.1 set基本概念
      • 3.8.2 set构造和赋值
      • 3.8.3 set大小和交换(p224)
      • 3.8.4 set插入和删除(p225)
      • 3.8.5 set查找和统计(p226)
      • 3.8.6 set和multiset区别(p227)
      • 3.8.7 pair对组创建(p228)
    • 3.8.8 set容器排序(p229~p230)
    • 3.9 map/multimap容器(p231)
      • 3.9.1 map基本概念
      • 3.9.2 map构造和赋值
      • 3.9.3 map大小和交换(p232)
      • 3.9.4 map插入和删除
      • 3.9.5 map查找和统计(p234)
      • 3.9.6 map的容器排序
    • 3.10 案例-员工分组(p236)
      • 3.10.1 案例描述
      • 3.10.2 实现步骤
  • 4 STL-函数对象(p237)
    • 4.1 函数对象
      • 4.1.1 函数对象概念
      • 4.1.2 函数对象使用
    • 4.2 谓词(p238)
      • 4.2.1 谓词概念
      • 4.2.2 一元谓词
      • 4.2.3 二元谓词(p239)
    • 4.3 内建函数对象(p240)
      • 4.3.1 内建函数对象意义
      • 4.3.2 算术仿函数
      • 4.3.3 关系仿函数
      • 4.3.4 逻辑仿函数(p242)

本文接
【C++】【第一篇】【黑马 p84 - p105 】C++学习记录
【C++】【第二篇】【黑马 p106~p126】C++学习记录
【C++】【第三篇】【黑马 p127 - p146 】C++学习记录
【C++】【第四篇】【黑马 p147 ~ p166】C++学习记录之职工管理系统
【C++】【第五篇】【黑马 p167~p184】C++学习记录之C++提高编程
【C++】【第六篇-1】【黑马 p185~p214】C++学习记录之C++提高编程

3.7 list容器(p215) 3.7.1 list基本概念
  • 功能:将数据进行链式存储
  • 链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
  • 链表的组成:链表由一些列结点组成
  • 结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
  • STL中的链表是一个双向循环链表
  • 链表的优点:可以对任意位置快速插入或删除元素
  1. 采用动态存储分配,不会造成内存浪费和溢出
  2. 链表执行插入和删除 *** 作十分方便,修改指针即可,不需要移动大量元素
  • 链表的缺点:对容器的遍历速度,没有数组快,占用的空间会比数组大
  1. 链表灵活,但是空间(指针域)和时间(遍历)额外消耗很大
  • 由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
  • List有一个重要的性质:插入 *** 作和删除 *** 作都不会造成原有list迭代器的失效,这在vector是不成立的
  • 总结:STL重List和vector是两个最常被使用的容器,各有优缺点
3.7.2 list构造函数
  • 功能描述:创建list容器
  • 函数原型
  1. list lst; //list采用模板类实现,对象的默认构造形式
  2. list(beg, end);//构造函数将(beg, end)区间中的元素拷贝给本身
  3. list(n, elem);//构造函数将n个elem拷贝给本身
  4. list(const list &lst);//拷贝构造函数
#include 
using namespace std;
#include

//list容器的构造函数

void printList(const list<int>&L)
{
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	//创建list容器
	list<int> L1;//默认构造

	//添加容器
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	//遍历容器
	printList(L1);

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

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

	//n个elem
	list<int>L4(10, 1000);
	printList(L4);

}

int main()
{
	test01();
	system("pause");
	return 0;
}
  • 总结:list构造方式同其他几个STL常用容器,熟练掌握即可
3.7.3 list赋值和交换(p217)
  • 功能描述:给list容器进行赋值,以及交换list容器
  • 函数原型
  1. assign(beg, end);//将(beg,end)区间中的数据拷贝赋值给本身
  2. assign(n, elem);//将n个elem拷贝赋值给本身
  3. list& operator=(const list &lst);//重载等号 *** 作符
  4. swap(lst);//将lst与本身的元素互换
#include 
using namespace std;
#include

//list容器的构造函数

void printList(const list<int>&L)
{
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	list<int>L1;

	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	printList(L1);

	list<int>L2;
	L2 = L1;//operator=赋值
	printList(L2);

	list<int>L3;
	L3.assign(L2.begin(), L2.end());
	printList(L3);

	list<int>L4;
	L4.assign(10, 100);
	printList(L4);


}

//交换
void test02()
{
	list<int>L1;

	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	
	list<int>L2;
	L2.assign(10, 100);
	printList(L1);
	printList(L2);
	cout << "-----------------" << endl;

	L1.swap(L2);
	printList(L1);
	printList(L2);
}

int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}
  • 总结:list赋值和交换 *** 作能够灵活运用即可
3.7.4 list大小 *** 作(p218)
  • 功能描述:对list容器的大小进行 *** 作
  • 函数原型
  1. size()//返回容器中与元素的个数
  2. empty();//判断容器是否为空
  3. resize(num);//重新指定容器的长度和num,若容器变长,则以默认值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
  4. resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
#include 
using namespace std;
#include

//list容器的大小 *** 作

void printList(const list<int>&L)
{
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	list<int>L1;

	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	printList(L1);

	//判断容器是否为空
	if (L1.empty())
	{
		cout << "L1为空" << endl;
	}
	else
	{
		cout << "L1不为空" << endl;
		cout << "L1的元素个数为: " << L1.size() << endl;
	}

	//重新指定大小
	L1.resize(10);
	printList(L1);

	L1.resize(15,1000);
	printList(L1);

}


int main()
{
	test01();
	system("pause");
	return 0;
}
  • 总结:
  1. 判断是否为空----empty
  2. 返回元素个数—size
  3. 重新指定个数—resize
3.7.5 list插入和删除(p219)
  • 功能描述:对list容器进行数据的插入和删除
  • 函数原型
  1. push_back(elem);//在容器尾部加入一个元素
  2. pop_back();//删除容器最后一个元素
  3. push_front(elem);//在容器开头插入一个元素
  4. pop_front();//从容器开头移除第一个元素
  5. insert(pos, elem);//在pos位置插elem元素的拷贝,返回新数据的位置
  6. insert(pos, n, elem);//在pos位置插入n个elem元素,无返回值
  7. insert(pos, beg, end);//在pos位置插入[beg, end)区间的数据,无返回值
  8. clear();//移除容器的所有数据
  9. erase(beg, end);//删除[beg, end)区间的数据,返回下一个数据的位置
  10. erase(pos);//删除pos位置的数据,返回下一个数据的位置
  11. remove(elem);//删除容器中所有与elen值匹配的元素
#include 
using namespace std;
#include

//list容器的插入和删除
/*
1. `push_back(elem);//在容器尾部加入一个元素`
2. `pop_back();//删除容器最后一个元素`
3. `push_front(elem);//在容器开头插入一个元素`
4. `pop_front();//从容器开头移除第一个元素`
5. `insert(pos, elem);//在pos位置插elem元素的拷贝,返回新数据的位置`
6. `insert(pos, n, elem);//在pos位置插入n个elem元素,无返回值`
7. `insert(pos, beg, end);//在pos位置插入[beg, end)区间的数据,无返回值`
8. `clear();//移除容器的所有数据`
9. `erase(beg, end);//删除[beg, end)区间的数据,返回下一个数据的位置`
10. `erase(pos);//删除pos位置的数据,返回下一个数据的位置`
11. `remove(elem);//删除容器中所有与elen值匹配的元素`
*/
void printList(const list<int>&L)
{
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	list<int>L;

	//尾插
	L.push_back(10);
	L.push_back(20);
	L.push_back(30);

	//头插
	L.push_front(100);
	L.push_front(200);
	L.push_front(300);
	printList(L);

	//尾删
	L.pop_back();
	printList(L);

	//头删
	L.pop_front();
	printList(L);

	//insert插入
	L.insert(L.begin(), 1000);
	printList(L);

	list<int>::iterator it = L.begin();
	L.insert(++it, 2000);
	printList(L);

	//删除
	it = L.begin();
	L.erase(it);
	printList(L);

	//移除
	L.push_back(10000);
	L.push_back(10000);
	printList(L);
	L.remove(10000);
	printList(L);

	//清空
	L.clear();
	printList(L);
}


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

输出为:

300 200 100 10 20 30
300 200 100 10 20
200 100 10 20
1000 200 100 10 20
1000 2000 200 100 10 20
2000 200 100 10 20
2000 200 100 10 20 10000 10000
2000 200 100 10 20
3.7.6 list数据存取(p220)
  • 功能描述:对list容器中数据进行存取
  • 由于list是链表,不是连续的存储空间, 所有不能使用at或者[]访问,不支持随机访问,只能顺序访问可看代码学习
  • 函数原型:
  1. front();//返回第一个元素
  2. back();//返回最后一个元素
#include 
using namespace std;
#include

//list容器的数据存取
void printList(const list<int>&L)
{
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	//原因list本质是链表,不是用连续的线性空间存储数据,迭代器也是不支持随机访问的
	//L1[0];//不可以用[]访问list中的元素
	//L1.at(0);//不可以用at访问

	cout << "第一个元素为: " << L1.front() << endl;
	cout << "最后一个元素为: " << L1.back() << endl;

	//验证迭代器不支持随机访问的
	list<int>::iterator it = L1.begin();
	it++;
	it--;
	//it = it + 1;//不允许这样,只允许++或者--
}


int main()
{
	test01();
	system("pause");
	return 0;
}
  • 总结:
  1. list容器中不可以 通过[]或者at方式访问数据
  2. 返回第一个元素 —front
  3. 返回最后一个元素 ----back
3.7.7 list反转和排序(p221)
  • 功能描述:将容器中的元素反转,以及将容器中的数据进行排序
  • 函数原型:
  1. reverse();//反转链表
  2. sort();//链表排序
  • 所有不支持随机访问迭代器的容器不可以用标准的算法,但不支持随机访问迭代器的容器,内部会提供对应一些算法
  • 如果降序的话需要学会自己会自定义一个bool函数,指定排序规则
#include 
using namespace std;
#include
#include 

//list容器的反转和排序
void printList(const list<int>&L)
{
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	//反转
	list<int>L1;

	L1.push_back(50);
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(40);
	L1.push_back(30);
	cout << "反转前:" << endl;
	printList(L1);

	//反转
	L1.reverse();
	cout<< "反转后: "<< endl;
	printList(L1);
}

bool myCompare(int v1, int v2)
{
	//降序 就让第一个数 > 第二个数
	return v1 > v2;
}

void test02()
{
	list<int>L1;

	L1.push_back(50);
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(40);
	L1.push_back(30);
	cout << "排序前前:" << endl;
	printList(L1);

	//所有不支持随机访问迭代器的容器不可以用标准的算法
	//不支持随机访问迭代器的容器,内部会提供对应一些算法
	//sort(L1.begin(), L1.end());//报错
	L1.sort();//默认排序规则  从小到大 升序
	cout << "排序后:" << endl;
	cout << "反转前:" << endl;
	printList(L1);

	L1.sort(myCompare);
	printList(L1);

}

int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}
3.7.8 排序案例(p222)
  • 案例描述:将Person自定义数据类型进行排序,Person中属有姓名,年龄和身高
  • 排序规则,按照年龄进行升序,如果年龄相同按照身高进行降序
#include 
using namespace std;
#include 

//list容器 排序案例 对于自定义数据类型 做排序

//按照年龄进行升序,如果年龄相同,按照身高进行降序

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 compaerPerson(Person &p1, Person& p2)
{
	if (p1.m_Age == p2.m_Height)
	{
		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("赵云", 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;
	cout << "排序后: " << endl;

	L.sort(compaerPerson);
	for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age
			<< " 身高: " << it->m_Height << endl;
	}

}

int main()
{
	test01();

	system("pause");
	return 0;
}
3.8 set/multiset 容器(p223) 3.8.1 set基本概念
  • 简介:所有元素都会在插入时自动被排序,也叫集合容器
  • 本质:set/multiset属于关联式容器,底层结构是用二叉树实现
  • set和mulitiset区别:
  1. set不允许容器中有重复的元素
  2. multiset允许容器中有重复的元素
3.8.2 set构造和赋值
  • 功能描述:创建set容器以及赋值
  • 构造:
  1. set st;//默认构造函数
  2. set(const set &st);//拷贝构造函数
  • 赋值:set& operator=(const set &st);//重载等号 *** 作符
#include 
using namespace std;
#include 

//set容器构造和赋值

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(30);
	s1.insert(20);
	s1.insert(30);
	s1.insert(40);

	//遍历容器
	//set容器特点,所有元素插入时候自动被排序
	//set容器不允许插入重复值
	printSet(s1);

	//拷贝构造
	set<int> s2(s1);
	printSet(s2);

	//赋值
	set<int> s3;
	s3 = s2;
	printSet(s3);

}

int main()
{
	test01();

	system("pause");
	return 0;
}
  • 总结
  1. set容器插入数据时用insert
  2. set容器插入数据的数据会自动排序
3.8.3 set大小和交换(p224)
  • 功能描述:统计set容器大小以及交换set容器
  • 函数原型:
  1. size();//返回容器中元素的数目
  2. empty();//判断容器是否为空
  3. swap(st);//交换两个集合容器
#include 
using namespace std;
#include 

//set的大小和交换

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(30);
	s1.insert(20);
	s1.insert(30);
	s1.insert(40);
	printSet(s1);

	//判断是否为空
	if (s1.empty())
	{
		cout << "s1为空" << endl;
	}
	else
	{
		cout << "s1不为空" << endl;
	}

	//插入数据
	set<int> s2;
	s2.insert(100);
	s2.insert(300);
	s2.insert(200);
	s2.insert(300);
	s2.insert(400);
	cout << "交换前: " << endl;
	printSet(s1);
	printSet(s2);
	cout << "交换后: " << endl;
	s1.swap(s2);
	printSet(s1);
	printSet(s2);

}

int main()
{
	test01();

	system("pause");
	return 0;
}

输出为:

10 20 30 40
s1不为空
交换前:
10 20 30 40
100 200 300 400
交换后:
100 200 300 400
10 20 30 40
  • 总结:
  1. 统计大小—size
  2. 判断是否为空—empty
  3. 交换容器—swap
3.8.4 set插入和删除(p225)
  • 功能描述: set容器进行插入数据和删除数据
  • 函数原型:
  1. insert(elem);//在容器中插入元素
  2. clear();//清楚所有元素
  3. erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
  4. erase(beg, end);//删除区间[beg, end)的所有元素,返回下一个元素的迭代器
  5. erase(elem);//删除容器中值为elem的元素
#include 
using namespace std;
#include 

//set容器的插入和删除

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;

	//插入
	s1.insert(30);
	s1.insert(20);
	s1.insert(60);
	s1.insert(50);
	printSet(s1);

	//删除
	s1.erase(s1.begin());
	printSet(s1);

	//删除重载版本
	s1.erase(60);
	printSet(s1);

	//清空
	s1.clear();
	printSet(s1);


}

int main()
{
	test01();

	system("pause");
	return 0;
}

输出结果为:

20 30 50 60
30 50 60
30 50

  • 总结:
  1. 插入—insert
  2. 删除—erase
  3. 清空—clear
3.8.5 set查找和统计(p226)
  • 功能描述:对set容器进行查找数据及统计数据
  • 函数原型:
  1. find(key);//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
  2. count(key);//统计key的元素个数
  • 对于set而言,统计的结果要么是0,要么是1
#include 
using namespace std;
#include 

//set容器的查找和统计

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;

	//插入
	s1.insert(30);
	s1.insert(20);
	s1.insert(60);
	s1.insert(50);
	printSet(s1);

	//查找
	set<int>::iterator pos = s1.find(30);

	if (pos != s1.end())
	{
		cout << "找到元素: " << *pos << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}

	//统计
	int num = s1.count(30);
	//对于set而言,统计的结果要么是0,要么是1
	cout << "num = " << num << endl;

}

int main()
{
	test01();

	system("pause");
	return 0;
}

输出结果为:

20 30 50 60
找到元素: 30
num = 1
  • 总结
  1. 查找—find (返回的是迭代器)
  2. 统计—count(对于set,结果为0或者1)
3.8.6 set和multiset区别(p227)
  • 学习目标: 掌握set和multiset的区别
  • 区别:
  1. set不可以插入重复数据,而multiset可以
  2. set插入数据的同时会返回插入结果,表示插入是否成功
  3. multiset不会检测数据,因此可以插入重复数据
#include 
using namespace std;
#include 

//set容器 和multiset的区别

void test01()
{
	set<int> s;

	pair<set<int>::iterator,bool> ret = s.insert(10);

	if (ret.second)
	{
		cout << "第一次插入成功" << endl;
	}
	else
	{
		cout << "第一次插入失败" << endl;
	}
	ret = s.insert(10);

	if (ret.second)
	{
		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;

}

int main()
{
	test01();

	system("pause");
	return 0;
}

输出结果为:

第一次插入成功
第一次插入失败
10 10
  • 总结:
  1. 如果不允许插入重复数据可以利用set
  2. 如果需要插入重复数据利用multiset
3.8.7 pair对组创建(p228)
  • 功能描述:成对出现的数据,利用对组可以返回两个数据
  • 两种创建方式:
  1. pair p (value1, value2);
  2. pair p = make_pair(value1, value2);
#include 
using namespace std;

//pair对组的创建

void test01()
{
	//第一种方式
	pair<string, int> p("Tom", 20);

	cout << "姓名:" << p.first << "年龄: " << p.second << endl;

	//第二种方式
	pair<string, int>p2 = make_pair("Jim", 20);
	cout << "姓名: " << p2.first << "年龄:" << p2.second << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

总结: 两种方式都可以创建对组,记住一种即可

3.8.8 set容器排序(p229~p230)
  • 学习目标:set容器默认排序规则为从小到大,掌握如何改变排序规则
  • 主要技术点:利用仿函数,可以改变排序规则
  • 对于内置函数的自定义排序规则
#include 
using namespace std;
#include 
//set容器排序

class MyCompare
{
public:
	/*
	非静态成员函数后面加const(加到非成员函数或静态成员后面会产生编译错误),
	表示成员函数隐含传入的this指针为const指针,决定了在该成员函数中,
	任意修改它所在的类的成员的 *** 作都是不允许的(因为隐含了对this指针的const引用);
	例外是对于mutable修饰的成员,加了const的成员函数可以被非const对象和const对象调用,
	但是不加const的成员函数只能被非const对象调用
	*/
	bool operator()(int v1, int v2) const
	{
		return v1 > v2;
	}
};

void test01()
{
	set<int>s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);
	s1.insert(50);

	for (set<int>::iterator it = s1.begin(); it != s1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	//指定排序规则为从大到小
	set<int, MyCompare>s2;
	s2.insert(10);
	s2.insert(30);
	s2.insert(20);
	s2.insert(40);
	s2.insert(50);

	for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

输出结果为:

10 20 30 40 50
50 40 30 20 10
  • 总结:利用仿函数可以指定set容器的排序规则

  • 对于自定义类型的自定义排序规则
#include 
using namespace std;
#include 

//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 test01()
{
	//自定义的数据类型,都会指定排序规则
	set<Person,comparePerson> s;
	//创建Person对象
	Person p1("p1", 10);
	Person p2("p2", 20);
	Person p3("p3", 30);
	Person p4("p4", 40);

	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 << "name: " << (*it).m_Name << ", age: " << it->m_Age << endl;
	}

}
int main()
{
	test01();

	system("pause");
	return 0;
}

输出结果为:

name: p4, age: 40
name: p3, age: 30
name: p2, age: 20
name: p1, age: 10
  • 总结: 对于自定义数据类型,set必须指定排序规则才可以插入数据
3.9 map/multimap容器(p231) 3.9.1 map基本概念
  • 简介:
  1. map中所有元素都是pair
  2. pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
  3. 所有元素都会根据元素的键值自动排序
  • 本质:map/multimap属于关联式容器,底层结构是用二叉树实现
  • 优点:可以根据key值快速找到value值
  • map和multimap的区别:
  1. map不允许容器中有重复key值元素
  2. multimap允许容器中有重复key值元素
3.9.2 map构造和赋值
  • 功能描述: 对map容器进行构造和赋值 *** 作
  • 函数原型:
  • 构造:
  1. map mp;//map默认构造函数
  2. map(const map &map);//拷贝构造函数
  • 赋值
  1. map& operator=(const map &mp);//重载等号 *** 作符
#include 
using namespace std;
#include 
//map容器 构造和赋值

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;
	}
	cout << endl;
}

void test01()
{
	//创建map容器
	map<int, int> m;

	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(4, 40));
	printMap(m);

	//拷贝构造
	map<int, int >m2(m);
	printMap(m2);

	//赋值
	map<int, int>m3 = m2;
	printMap(m3);


}
int main()
{
	test01();

	system("pause");
	return 0;
}
  • 总结: map容器中所有元素都是成对出现,插入数据时候需要使用对组
3.9.3 map大小和交换(p232)
  • 功能描述:统计map容器大小以及交换map容器
  • 函数原型
  1. size();//返回容器中元素的数目
  2. empty();//判断容器是否为空
  3. swap(st);//交换两个集合容器
3.9.4 map插入和删除
  • 功能描述: map容器进行插入数据和删除数据
  • 函数原型
  1. insert(elem);//在容器中插入元素
  2. clear();//清除所有元素
  3. erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
  4. erase(beg, end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器
  5. erase(key);//删除容器中值为key的元素
#include 
using namespace std;
#include 
//map容器 的插入和删除

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;
	}
	cout << endl;
}

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, 3));

	//第四种,不建议,查错了会自动创建一个,
	//但是[]可以利用key访问到value
	m[4] = 40;

	cout << m[5] << endl;

	printMap(m);

	//删除
	m.erase(m.begin());
	printMap(m);

	m.erase(3);
	printMap(m);

	m.clear();
	printMap(m);
}
int main()
{
	test01();

	system("pause");
	return 0;
}

输出为:

0
key = 1value = 10
key = 2value = 20
key = 3value = 3
key = 4value = 40
key = 5value = 0

key = 2value = 20
key = 3value = 3
key = 4value = 40
key = 5value = 0

key = 2value = 20
key = 4value = 40
key = 5value = 0
  • 总结:
  1. map插入方式很多,记住其一即可
  2. 插入—insert
  3. 删除—erase
  4. 清空—clear
3.9.5 map查找和统计(p234)
  • 功能描述:对map容器进行查找数据以及统计数据
  • 函数原型:
  1. find(key);//查找key是否存在,返回该键的元素的迭代器,若不存在,返回set.end();
  2. count(key);//统计key元素个数
#include 
using namespace std;
#include 
//map容器 的查找和统计

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;
	}
	cout << endl;
}

void test01()
{
	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));

	map<int,int>::iterator pos= m.find(3);
	if (pos != m.end())
	{
		cout << "查到了元素key = " << (*pos).first << "value = " << pos->second << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}

	//统计
	//map不允许插入重复的元素,结果要么是0,要么是1
	//multimap的count统计可以大于1
	int num = m.count(3);
	cout << "num = " << num << endl;

}

int main()
{
	test01();

	system("pause");
	return 0;
}

输出为:

查到了元素key = 3value = 30
num = 1
  • 总结:
  1. 查找 — find (返回的是迭代器)
  2. 统计 — count(对于map,结果为0或者1)
3.9.6 map的容器排序
  • 学习目标:map容器默认排序规则为按照key值进行从小到大排序,掌握如何改变排序规则
  • 主要技术点:利用仿函数,可以改变排序规则
#include 
using namespace std;
#include 
//map容器 排序

class MyCompare
{
public:
	bool operator()(int v1, int v2) const 
	{
		//降序
		return v1 > v2;
	}
};

void printMap(map<int, int, MyCompare>& m)
{
	for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << (*it).first << "value = " << it->second << " " << endl;
	}
	cout << endl;
}

void test01()
{
	map<int, int, MyCompare>m;
	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(5, 50));
	m.insert(make_pair(4, 40));
	printMap(m);


}

int main()
{
	test01();

	system("pause");
	return 0;
}

输出结果为:

key = 5value = 50
key = 4value = 40
key = 3value = 30
key = 2value = 20
key = 1value = 10

总结:

  1. 利用仿函数可以指定map容器的排序规则
  2. 对于自定义数据类型,map必须指定排序规则,同set容器
3.10 案例-员工分组(p236) 3.10.1 案例描述
  • 公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
  • 员工信息有:姓名,工资组成;部分分为:策划、美术、研发
  • 随机给10名员工分配部门和工资
  • 通过multimap进行信息的插入 key(部门编号)value(员工)
  • 分部门显示员工信息
3.10.2 实现步骤
  1. 创建10名员工,放到vector中
  2. 遍历vector容器,取出每个员工,进行随机分组
  3. 分组后,将员工部门编号作为key,具体员工为value,放入到multimap容器中
  4. 分部门显示员工信息
#include 
using namespace std;
#include 
#include 
#include 

#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() % 10001 + 10000;//10000~19999
		w.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 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; pos++, index++)
	{
		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; pos++, index++)
	{
		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; pos++, index++)
	{
		cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl;
	}
	cout << endl;

}

void test01()
{
	//1、创建员工
	vector<Worker> vworker;
	createWorker(vworker);

	//测试
	for (vector<Worker>::iterator it = vworker.begin(); it != vworker.end(); it++)
	{
		cout << "姓名: " << it->m_Name << " 工资: " << it->m_Salary << endl;
 	}
	cout << endl;

	//2、员工分组
	multimap<int, Worker>mworker;
	setGroup(vworker, mworker);

	//3、分组显示员工
	showWorkerByGroup(mworker);
}

int main()
{
	srand((unsigned int)time(NULL));
	test01();

	system("pause");
	return 0;
}

输出为:

姓名: 员工A 工资: 10020
姓名: 员工B 工资: 15284
姓名: 员工C 工资: 11772
姓名: 员工D 工资: 15540
姓名: 员工E 工资: 16222
姓名: 员工F 工资: 17416
姓名: 员工G 工资: 19501
姓名: 员工H 工资: 15829
姓名: 员工I 工资: 19513
姓名: 员工J 工资: 12433

策划部份:
姓名: 员工B 工资: 15284
姓名: 员工D 工资: 15540
姓名: 员工E 工资: 16222
姓名: 员工G 工资: 19501
姓名: 员工H 工资: 15829
姓名: 员工J 工资: 12433

美术部份:
姓名: 员工F 工资: 17416
姓名: 员工I 工资: 19513

研发部份:
姓名: 员工A 工资: 10020
姓名: 员工C 工资: 11772
4 STL-函数对象(p237) 4.1 函数对象 4.1.1 函数对象概念
  • 概念:
  1. 重载函数调用 *** 作符的类,其对象常称为函数对象
  2. 函数对象使用重载的()时,行为类似函数调用,也叫仿函数
  • 本质:函数对象(仿函数)是一个类,不是一个函数
4.1.2 函数对象使用
  • 特点:
  1. 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
  2. 函数对象超出普遍函数的概念,函数对象可以有自己的状态
  3. 函数对象可以作为参数传递
#include 
using namespace std;


class MyAdd
{
public:
	int operator()(int v1, int v2)
	{
		return v1 + v2;
	}

};

//1、函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
void test01()
{
	MyAdd myAdd;
	cout << myAdd(10, 10) << endl;
}

//2、函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint
{
public:
	MyPrint()
	{
		this->count = 0;
	}
	void operator()(string test)
	{
		cout << test << endl;
		this->count++;
	}
	int count;//内部自己状态
};

void test02()
{
	MyPrint myprint;
	myprint("Hello World");
	myprint("Hello World");
	myprint("Hello World");
	myprint("Hello World");

	cout << "mypring调用的次数: " << myprint.count << endl;
}

//3、函数对象可以作为参数传递
void doPrint(MyPrint &mp, string test )
{
	mp(test);
}

void test03()
{
	MyPrint myprint;
	doPrint(myprint, "Hello World!");

}

int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

输出为:

20
Hello World
Hello World
Hello World
Hello World
mypring调用的次数: 4
Hello World!
请按任意键继续. . .

  • 总结: 仿函数的写法非常灵活,可以作为参数进行传递
4.2 谓词(p238) 4.2.1 谓词概念
  • 概念:
  1. 返回bool类型的仿函数称为谓词
  2. 如果operator()接受一个参数,那么叫做一元谓词
  3. 如果operator()接受两个参数,那么叫做二元谓词
4.2.2 一元谓词
  • GreaterFive()匿名的函数对象
#include 
using namespace std;
#include 
#include 
//一元谓词

class GreaterFive
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}
};

void test01()
{
	vector<int>v;
	for (int i = 0; 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 << "找到了大于5的数字为: " << *it << endl;
	}
}

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

输出结果为:

找到了大于5的数字为: 6
  • 总结: 参数只有一个的谓词,称为一元谓词
4.2.3 二元谓词(p239)
#include 
using namespace std;
#include 
#include 
//二元谓词

class MyCompare
{
public:
	bool operator()(int val1, int val2)
	{
		return val1 > val2;
	}
};

void test01()
{
	vector<int>v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(30);
	v.push_back(20);
	v.push_back(50);

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

	//使用函数对象 改变算法策略,变为排序规则为 从大到小
	sort(v.begin(), v.end(), MyCompare());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

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

输出结果为:

10 20 30 40 50
50 40 30 20 10
  • 总结:参数只有两个的谓词,称为二元谓词
4.3 内建函数对象(p240) 4.3.1 内建函数对象意义
  • 概念:STL内建了一些函数对象
  • 分类
  1. 算术仿函数
  2. 关系仿函数
  3. 逻辑仿函数
  • 用法:
  1. 这些仿函数所产生的对象,用法和一般函数完全相同
  2. 使用内建函数对象,需要引入头文件#include
4.3.2 算术仿函数
  • 功能描述
  1. 实现四则运算
  2. 其中negate是一元运算,其他的都是二元运算
  • 仿函数原型
  1. template T plus;//加法仿函数
  2. tempate T minus ;//减法仿函数
  3. template T multiplies;//乘法仿函数
  4. template T divides;//除法仿函数
  5. template T modulus;//取模仿函数
  6. template T negate;//取反仿函数
#include 
using namespace std;
#include//内建函数对象头文件

//内建函数对象 算术运算符

//negate 一元仿函数  取反仿函数
void test01()
{
	negate<int>n;

	cout << n(50) << endl;

}

//plus 二元仿函数  加法

void test02()
{
	plus<int>p;
	cout << p(10, 20) << endl;
}

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

输出结果为

-50
30
  • 总结: 使用内建函数对象时,需要引入头文件#include
4.3.3 关系仿函数
  • 功能描述:实现关系对比
  • 仿函数原型
  1. template bool equal_to;//等于
  2. template bool not_equal_to;//不等于
  3. template bool greater;//大于
  4. template bool greater_equal;//大于等于
  5. template bool less;//小于
  6. template bool less_equla;//小于等于
#include 
using namespace std;
#include//内建函数对象头文件
#include 
#include 

//内建函数对象 关于运算符

//greater 大于
void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(20);
	v.push_back(40);
	v.push_back(30);
	v.push_back(60);
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	//降序
	sort(v.begin(), v.end(), greater<int>());

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



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

输出结果为:

50 20 40 30 60
60 50 40 30 20
  • 总结:关系仿函数中最常用的就是greater<>大于
4.3.4 逻辑仿函数(p242)
  • 功能描述:实现逻辑运算
  • 函数原型
  1. template bool logical_and;//逻辑与
  2. template bool logical_or;//逻辑或
  3. template bool logical_not;//逻辑非
#include 
using namespace std;
#include 
#include 
#include 

//内建函数对象 逻辑仿函数

//逻辑非 logical_not
void test01()
{
	vector<bool> v;
	v.push_back(true);
	v.push_back(false);
	v.push_back(true);
	v.push_back(false);

	for (vector<bool>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	//利用逻辑非 将容器v 搬运到 容器v2中,并执行取反的 *** 作
	vector<bool>v2;
	v2.resize(v.size());

	transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
	for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}



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

输出结果为:

1 0 1 0
0 1 0 1
  • 总结: 逻辑仿函数实际应用较少,了解即可

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/579630.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)

保存