C++提高编程(泛型编程与STL技术)

目录

1.模板

1.1模板的概念

1.2函数模板

1.2.1函数模板语法

1.2.2函数模板注意事项

1.2.3函数模板案例

1.2.4普通函数与函数模板的区别

1.2.5普通函数与函数模板调用规则

1.2.6 模板的局限性

1.3类模板

1.3.1类模板语法

1.3.2类模板与函数模板区别

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

1.3.4类模板对象做函数参数

1.3.5类模板与继承

1.3.6类模板成员函数的类外实现

1.3.7类模板份文件编写

1.3.8类模板与友元

1.3.9类模板案例

2.STL初识

2.1STL诞生

2.2STL基本概念

2.3STL六大组件

2.4STL中容器、算法、迭代器

2.5容器算法迭代器初识

2.5.1vector存放内置数据类型

2.5.2vector存放自定义数据类型

 2.5.3vector容器嵌套容器

3.STL—常用容器

3.1string容器

3.1.1string基本概念

3.1.2string构造函数

3.1.3string赋值操作

3.1.4string字符串拼接

3.1.5string查找和替换

3.1.6string字符串比较

3.1.7string字符提取

3.1.8string插入和删除

3.1.9string子串

3.2vector容器

3.2.1vector基本概念

3.2.2vector构造函数

3.2.3vector赋值操作

3.2.4vector容量和大小

3.2.5vector插入和删除

3.2.6vector数据存取

3.2.7vector互换容器

 3.2.8vector预留空间

3.3deque容器

3.3.1deque容器基本概念

3.2.2deque构造函数

3.3.3deque赋值操作

3.3.4deque大小操作

3.3.5deque插入和删除

3.3.6deque数据存取

3.3.7deque排序

3.4案例——评委打分

3.4.1案例描述

3.4.2实现步骤

3.5stack容器

3.5.1stack基本概念

3.5.2stack容器常用接口

3.6queue容器

3.6.1queue基本概念

3.6.2queue常用接口

3.7list容器

3.7.1list基本概念

 3.7.2list构造函数

3.7.3list赋值和交换

3.7.3list大小操作

3.7.5list插入和删除

3.7.6list数据存取

3.7.7list反转和排序

3.7.8排序案例

3.8set/multiset容器

3.8.1set概念

3.8.2set的构造和赋值

3.8.3set大小和交换

3.8.4set插入和删除

3.8.5set查找和统计

3.8.6set和multiset区别

3.8.7pair队组创建

3.8.8set容器排序

3.9map/multimap容器

3.9.1map基本概念

3.9.2map构造和赋值

3.9.3map大小和交换

3.9.4map容器-插入和删除

3.9.5map查找和统计

3.9.6map容器排序

3.10案例-员工分组

3.10.1案例描述

4 STL函数对象

4.1函数对象

4.1.1函数对象概念

4.1.2函数对象使用

4.2谓词

4.2.1谓词概念

4.2.2一元谓词

4.2.3二元谓词

4.3内建函数对象

4.3.1内建函数对象意义

4.3.2算数仿函数

4.3.3关系仿函数

4.3.4逻辑仿函数

5STL—常用算法

5.1常用遍历算法

5.1.1for_each

5.1.2transform

5.2常用查找算法

5.2.1find

5.2.2find_if

5.2.3adjacent_find

5.2.4binary_search

5.2.5count

5.2.6count_if

5.3常用排序算法

5.3.1sort

5.3.2random_shuffle

5.3.3merge

5.3.4reverse

5.4常用拷贝和替换算法

5.4.1copy

5.4.2replace

5.4.3replace_if

5.4.4swap

5.5常用算术生成算法

5.5.1accumulate

5.5.2fill

5.6常用集合算法

5.6.1set_intersection

5.6.2set_union

5.6.3set_difference


1.模板

1.1模板的概念

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

1.2函数模板

  • C++编程思想主要有面向对象和泛型编程,泛型编程主要利用的技术就是模板
  • C++提供两种模板机制,函数模板类模板

1.2.1函数模板语法

函数模板作用:

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

语法:

template

解释:

template——声明创建模板

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

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

#include 
using namespace std;

//函数模板
//两个整形交换的函数
void SwapInt(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}
//交换两个浮点型函数
void SwapDouble(double &a, double &b)
{
	double temp = a;
	a = b;
	b = temp;
}
//函数模板
template//声明一个模板,告诉编译器后面板代码中紧跟的T不要报错,T是一个通用数据类型
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
void test01()
{
	int a = 10;
	int b = 20;
	SwapInt(a, b);
	cout << "a = " << a << ",b = " << b << endl;

	double  c = 1.1;
	double  d= 2.2;
	SwapDouble(c, d);
	cout << "c = " << c << ",d = " << d << endl;

	//利用函数模板交换
	//两种方式使用函数模板
	//1.自动类型推导
	mySwap(a, b);
	mySwap(c, d);
	cout << "a = " << a << ",b = " << b << endl;
	cout << "c = " << c << ",d = " << d << endl;
	///2.显式指定类型
	mySwap(a, b);
	cout << "a = " << a << ",b = " << b << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

1.2.2函数模板注意事项

注意事项:

  1. 自动类型推导,必须推导出一定的数据类型T,才可以使用
  2. 模板必须要确定出T的数据类型,才可以使用
#include 
using namespace std;

//函数模板注意事项
template                       //typename可以替换成class
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
//1.自动类型推导,必须推导出一定的数据类型T才可以使用

void test01()
{
	int a = 10;
	int b = 20;
	mySwap(a, b);            //正确
	cout << "a = " << a << ",b = " << b << endl;
	char c = 'c';
	//mySwap(a, c);          //错误,推导不出一致的T类型
}

//2.模板必须要确定出T的数据类型,才可以使用
template 
void func()
{
	cout << "func()调用" << endl;
}
void test02()
{
	//func();           //错误❌
	func();         //可以!
}

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

总结:

使用模板时,必须确定出通用数据类型T,并且能够推导出一致的类型。

1.2.3函数模板案例

案例描述:

  • 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序。
  • 排序规则从大到小,排序算法为选择排序
  • 分别使用char数组和int数组进行测试
#include 
using namespace std;
//实现通用 对数组进行排序的函数
//规则 从大到小
//算法 选择排序
//测试 char数组 int数组
//排序算法
//交换函数模板
template
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
//排序函数模板
template
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)
		{
			//交换max和i下标的元素
			mySwap(arr[max], arr[i]);
		}
	}
}
//打印数组模板
template
void printArray(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
void test01()
{
	//测试char数组
	char charArr[] = "badcfe";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}
void test02()
{
	//测试int数组
	int intArr[] = { 7,8,5,2,3,4,1 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

1.2.4普通函数与函数模板的区别

普通函数与模板函数的区别:

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显式指定类型的方式,可以发生隐式类型转换
#include 
using namespace std;
//普通函数与模板函数区别
//1.普通函数调用可以发生隐式类型转换
//2.函数模板 用自动类型推导,不可以发生隐式类型转换
//3.函数模板 用显示指定类型,可以发生隐式类型转换
//普通函数
int myAdd(int a, int b)
{
	return a + b;
}
//函数模板
template
T myAdd02(T a,T b)
{
	return a + b;
}
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << myAdd(a, b) << endl;
	cout << myAdd(a, c) << endl;     //c隐式转换为int类型

	//自动类型推导
	cout << myAdd02(a, b) << endl;
	//cout << myAdd02(a, c) << endl; //报错, 用自动类型推导,不可以发生隐式类型转换
	cout << myAdd02(a, c) << endl;//用显示指定类型,可以发生隐式类型转换
}
int main()
{
	test01();

	system("pause");
	return 0;
}

1.2.5普通函数与函数模板调用规则

调用规则如下:

  1. 如果函数模板和普通模板都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板
#include
using namespace std;

//普通函数与函数模板调用规则
//1.如果函数模板和普通模板都可以实现,优先调用普通函数
//2.可以通过空模板参数列表来强制调用函数模板
//3.函数模板也可以发生重载
//4.如果函数模板可以产生更好的匹配,优先调用函数模板

void myPrint(int a, int b)
{
	cout << "调用的是普通函数!" << endl;
}
template
void myPrint(T a, T b)
{
	cout << "调用的模板!" << endl;
}
template
void myPrint(T a,T b,T c)
{
	cout << "重载的模板!" << endl;
}
void test01()
{
	int a = 10;
	int b = 20;
	myPrint(10, 20);        //调用的时普通函数
	myPrint<>(a,b);        //通过空模板参数列表来强制调用函数模板
	myPrint<>(a, b,100);    //函数模板也可以发生重载
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2);        //如果函数模板可以产生更好的匹配,优先调用函数模板
} 
int main()
{
	test01();

	system("pause");
	return 0;
}

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

1.2.6 模板的局限性

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

例如:

template
void f(T a, T b)
{
	a = b;
}

在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了。

再例如:

template 
void f(T a, T b)
{
	if(a > b)
	{...... }
}

在上述代码中,如果T的数据类型是像Person这样的自定义数据类型,也无法正常运行。

因此C++为了解决这种问题,提供了模板的重载,可以为这些特定的类型提供具体化的模板

#include 
#include 
using namespace std;
//模板局限性
//模板并不是万能的有些特定数据类型,需要用具体化方式做特殊实现

class Person
{
public:
	Person(string name, int age)
	{
		m_age = age;
		m_name = name;
	}
	string m_name;
	int m_age;
};
//对比两个数是否相等
//普通函数模板
template
bool myCompare(T &a, T &b)
{
	if (a == b)
		return true;
	else
		return false;
}
//利用具体化的Person版本实现代码,具体化优先调用
template<> bool myCompare(Person &p1,Person &p2)
{
	if ((p1.m_name == p2.m_name) && (p1.m_age == p2.m_age))
		return true;
	else
		return false;
}
void test01()
{
	int a = 10;
	int b = 20;
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a = b" << endl;
	}
	else
	{
		cout << "a != b" << endl;
	}
}
void test02()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	bool ret = myCompare(p1, p2);
	if (ret)
		cout << "p1 = p2" << endl;
	else
		cout << "p1 != p2" << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

总结:

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

1.3类模板

1.3.1类模板语法

类模板作用:建立一个通用类,类中的成员、数据类型可以不具体制定,用一个虚拟的类型来代表。

语法:

template、
类

解释:

template——声明创建模板

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

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

示例:

#include 
#include 
using namespace std;
//类模板
template
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		m_age = age;
		m_name = name;
	}
	void showPerson()
	{
		cout <<"Name: "<< this->m_name << "\tAge: " < m_age << endl;
	}
public:
	NameType m_name;
	AgeType m_age;
};
void test01()
{
	//指定NameType为string类型,AgeType为int类型
	Person p1("孙悟空", 999);
	p1.showPerson();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板。

1.3.2类模板与函数模板区别

类模板与函数模板区别主要有两点:

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板参数列表中可以有默认参数,函数模板中不行
#include 
using namespace std;

//类模板与函数模板区别
template
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		m_Name = name;
		m_Age = age;
	}
	void showPerson()
	{
		cout << "Name: " << m_Name << "\tAge: " << m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};
//1.类模板没有自动类型推导的使用方式
void test01()
{
	//Person p("孙悟空", 1000);错误❌,无法用自动类型推导
	Person p("孙悟空", 1000);//正确,只能用显式指定类型
	p.showPerson();
}
//2.类模板在模板参数列表中可以有默认参数
void test02()
{
	Person p("猪八戒", 999);
	p.showPerson();
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

总结:

  1. 类模板没有自动类型推导的使用方式,只能用显式指定类型方式
  2. 类模板在模板参数列表中可以有默认参数

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

类模板中成员函数与普通类中成员函数创建时机是有区别的:

  1. 普通类中成员函数一开始就可以创建
  2. 类模板中的成员函数只有在调用时才创建
#include 
using namespace std;

//类模板中成员函数创建时机
//类模板中成员函数在调用时才创建
class Person1
{
public:
	//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};
class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show" << endl;
	}
};
template
class MyClass
{
public:
	T obj;
	//类模板中成员函数
	void func1()
	{
		obj.showPerson1();
	}
	void func2()
	{
		obj.showPerson2();
	}
};
void test01()
{
	MyClass m;
	m.func1();
	//m.func2();   //编译会出错,说明函数调用才会创建成员函数
	MyClass n;
	n.func2();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:类模板中成员函数并不是一开始就创建,而是在调用时才创建。

1.3.4类模板对象做函数参数

一共有三种传入方式:

  1. 指定传入类型——直接显示对象的数据类型
  2. 参数模板化——将对象中的参数变为模板进行传递
  3. 整个类模板化——将这个对象类型 模板化进行传递
#include 
using namespace std;
//类模板对象做函数参数
template
class Person
{
public:
	Person(T1 name, T2 age)
	{
		m_age = age;
		m_name = name;
	}
	void showPerson()
	{
		cout << "Name: " << m_name << "\tAge: " << m_age << endl;
	}
	T1 m_name;
	T2 m_age;
};
//1.指定传入类型——直接显示对象的数据类型
void printPerson1(Person& p)
{
	p.showPerson();
}
void test01()
{
	Person p("孙悟空", 100);
	printPerson1(p);
}
//2.参数模板化——将对象中的参数变为模板进行传递
template
void printPerson2(Person& p) 
{
	p.showPerson();
	cout << "T1的类型为:" << typeid(T1).name() << endl;
	cout << "T2的类型为:" << typeid(T2).name() << endl;

}
void test02()
{
	Person p("猪八戒", 100);
	printPerson2(p);
}
//3.整个类模板化——将这个对象类型 模板化进行传递
template
void printPerson3(T & p)
{
	p.showPerson();
	cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03()
{
	Personp("唐僧", 30);
	printPerson3(p);
}
int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

总结:

  1. 通过类模板创建的对象,可以有三种方式向函数中传参
  2. 使用广泛的是第一种:指定传入的类型

1.3.5类模板与继承

当类模板碰到继承时,需要注意以下几点:

  1. 当子类继承的父类是一个类模板时,子类在声明的时候,需要指定出父类中的T的类型
  2. 如果不指定,便编译器无法给子类分配内存
  3. 如果想灵活指定出父类中T的类型,子类也需要变为类模板
#include 
using namespace std;
//类模板与继承
template
class Base
{
	T m;
};
//class Son:public Base //错误❌,必须要知道父类中的T类型,才能继承给子类
class Son:public Base
{

};
void test01()
{
	Son s1;
}
//如果想灵活指定父类中T的类型,子类也需要变类模板
template
class Son2 :public Base
{
public:
	Son2()
	{
		cout << "T1的类型为: " << typeid(T1).name() << endl;
		cout << "T2的类型为: " << typeid(T2).name() << endl;
	}
	T1 obj;
};
void test02()
{
	Son2 S2;
}
int main()
{
	test02();

	system("pause");
	return 0;
}

1.3.6类模板成员函数的类外实现

#include 
using namespace std;
//类模板成员函数类外实现
template
class Person
{
public:
	Person(T1 name, T2 age);
	void showPerson();
	T1 m_Name;
	T2 m_Age;
};
//构造函数类外实现
template
Person::Person(T1 name, T2 age)
{
	m_Name = name;
	m_Age = age;
}
//成员函数类外实现
template
void Person::showPerson()
{
	cout << "Name: " << m_Name << "\tAge: " << m_Age << endl;
}
void test01()
{
	Personp("Tom", 20);
	p.showPerson();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:类模板中成员函数类外实现时,需要加上模板参数列表

1.3.7类模板份文件编写

问题:

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

解决:

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

person.hpp中代码

#pragma once
#include 
using namespace std;
//类模板文件编写问题以及解决方式
template
class Person
{
public:
	Person(T1 name, T2 age);
	void showPerson();

	T1 m_Name;
	T2 m_Age;
};
template
Person::Person(T1 name, T2 age)
{
	m_Age = age;
	m_Name = name;
}
template
void Person::showPerson()
{
	cout << "Name: " << m_Name << "\tAge: " << m_Age << endl;
}

#include 
//第一种解决方式,直接包含源文件
//#include "person.cpp"
//第二种解决方式,将.h和.cpp中内容写在一起,将后缀名改为.hpp文件
#include "person.hpp"
using namespace std;
void test01()
{
	Personp("Jerry", 18);
	p.showPerson();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp

1.3.8类模板与友元

学习目标:

掌握类模板配合友元函数的类内实现和类外实现

  • 全局函数内实现-直接在类内声明友元函数即可
  • 全局函数类外实现-需要提前让编译器直到全局函数的存在
#include 
using namespace std;
//通过全局函数打印Person信息

//提前让编译器知道Person类的存在
template
class Person;
//类外实现
template
void printPerson2(Person p)
{
	cout << "类外实现——Name: " << p.m_name << "\tAge: " << p.m_age << endl;
}
template
class Person
{
	//全局函数类内是实现
	friend void printPerson(Person p)
	{
		cout << "类内实现——Name: " << p.m_name << "\tAge: " <(Person p);
public:
	Person(T1 name, T2 age)
	{
		m_name = name;
		m_age = age;
	}
private:
	T1 m_name;
	T2 m_age;
};

//1.全局函数在类内实现
void test01()
{
	Person p("Tom", 20);
	printPerson(p);
}
//2.全局函数在类外实现
void test02()
{
	Person p("Jerry", 30);
	printPerson2(p);
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别。

1.3.9类模板案例

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

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

C++提高编程(泛型编程与STL技术)_第1张图片

 示例:

myArray16.hpp代码如下:

#pragma once
#include 
using namespace std;

template
class MyArray
{
public:
	//有参构造  参数 容量
	MyArray(int Capacity)
	{
		m_Capacity = Capacity;
		m_Size = 0;
		pAddress = new T[Capacity];
	}
	//拷贝构造
	MyArray(const MyArray& arr)
	{
		pAddress = new T[arr.m_Capacity];
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		for (int i = 0; i < m_Size; i++)
		{
			pAddress[i] = arr.pAddress[i];
		}
		
	}
	//operator= 防止浅拷贝问题 a = b = c
	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];
		for (int i = 0; i < m_Size; i++)
		{
			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 (pAddress != NULL)
		{
			delete[] pAddress;
			pAddress = NULL;
		}
	}
private:
	T* pAddress;   //指针指向堆区开辟的真实数组
	int m_Capacity;  //数组容量
	int m_Size;       //数组大小
};

测试代码:

#include 
using namespace std;
#include "myArray16.hpp"
void printIntArray(MyArray & arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << arr[i] << endl;
	}
}
void test01()
{
	MyArray arr1(5);
	for (int i = 0; i < 5; i++)
	{
		arr1.Push_Back(i);    //利用尾插法向数组中插入数据
	}
	cout << "arr1的打印输出为:" << endl;
	printIntArray(arr1);
	cout << "arr1的容量为:" < arr2(arr1);
	cout << "arr2的打印输出为:" << endl;
	printIntArray(arr2);
	//尾删
	arr2.Pop_Back();
	cout << "arr2的容量为:" << arr2.getCapacity() << endl;
	cout << "arr2的大小为:" << arr2.getSize() << endl;
	cout << "arr2的打印输出为:" << endl;
	printIntArray(arr2);
}
//测试自定义数据类型
class Person
{
public:
	Person() {};
	Person(string name, int age)
	{
		this->m_age = age;
		this->m_name = name;
	}
	string m_name;
	int m_age;
};
void printPersonArray(MyArray& arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << "Name: " << arr[i].m_name << "\tAge: " << arr[i].m_age << endl;
	}
}
void test02()
{
	MyArray arr(10);
	Person p1("孙悟空", 999);
	Person p2("韩信", 20);
	Person p3("妲己", 50);
	Person p4("安琪拉", 99);
	Person p5("赵云", 9);
	//将数据插入到数组中
	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.1STL诞生

  • 长久以来,软件界一直希望建立一种可重复利用的东西
  • C++的面向对象和泛型编程思想,目的就是复用性的提升
  • 大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
  • 为建立数据结构和算法的一套标准,诞生了STL

2.2STL基本概念

  • STL(Standard Template Library,标准模板库)
  • STL从广义上分为:容器(container)算法(algorithm)、迭代器(iterator)
  • 容器算法之间通过迭代器进行无缝连接
  • STL几乎所有代码都采用了模板类或模板函数

2.3STL六大组件

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

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

2.4STL中容器、算法、迭代器

容器:置物之所也。

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

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

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

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

算法:问题之解法也。

有限的步骤,解决逻辑或数学上的问题。

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

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

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

迭代器:容器和算法之间粘合剂。

提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器内部表示方式。

每个容器都有自己的专属迭代器。

迭代器非常类似于指针,初学阶段可以先理解迭代器为指针。

迭代器种类:

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

2.5容器算法迭代器初识

2.5.1vector存放内置数据类型

容器:vector

算法:for_each

迭代器:vector::iterator

示例:

#include 
#include 
#include 
using namespace std;

void myPrint(int val)
{
	cout << val << endl;
}
//vector容器存放内置数据类型
void test01()
{
	//创建了一个vector容器,数组
	vector v;
	//向容器中插入数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	//通过迭代器访问容器中的数据
	//vector::iterator itBeign = v.begin();//起始迭代器,指向容器中第一个元素
	//vector::iterator itEnd = v.end();  //结束迭代器,指向容器中最后一个元素的下一个位置

	第一种遍历方式
	//while (itBeign != itEnd)
	//{
	//	cout << *itBeign << endl;
	//	itBeign++;
	//}

	//第二种遍历方式
	/*for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << endl;
	}*/
	
	//第三种遍历方式 利用STL中提供的遍历算法
	for_each(v.begin(), v.end(), myPrint);

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

2.5.2vector存放自定义数据类型

#include 
#include 
using namespace std;
//vector容器中存放自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}
	string m_name;
	int m_age;
};
void test01()
{
	vector v;
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);

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

	//遍历容器中的数据
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名: " << (*it).m_name << "\t年龄:" << (*it).m_age << endl;
	}
	//*it类型就是尖括号内类型 这里*it类型就是Person类型
}
//存放自定义数据类型指针
void test02()
{
	vector v;
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);

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

	//遍历容器
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名: " << (*it)->m_name << "\t年龄:" << (*it)->m_age << endl;
	}
	//*it类型就是尖括号内类型 这里*it类型就是Person*类型
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

 2.5.3vector容器嵌套容器

#include 
using namespace std;
#include 
//容器嵌套容器
void test01()
{
	vector>v;

	//创建小容器
	vectorv1;
	vectorv2;
	vectorv3;
	vectorv4;
	//向小容器中添加数据
	for (int i = 0; i < 4; i++)
	{
		v1.push_back(i + 1);
		v2.push_back(i + 2);
		v3.push_back(i + 3);
		v4.push_back(i + 4);
	}
	//将小容插入到大的容器中
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);
	//通过大容器,把所有数据遍历一遍
	for (vector>::iterator it = v.begin(); it != v.end(); it++)
	{
		//(*it)======容器vector
		for (vector::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
		{
			cout << *vit << " ";
		}
		cout << endl;
	}
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.STL—常用容器

3.1string容器

3.1.1string基本概念

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

string和char*区别:

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

特点:

string内部封装了很多成员方法

例如,查找find、拷贝copy、删除delete、替换replace、插入insert

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

3.1.2string构造函数

构造函数原型:

  • string();      //创建了一个空的字符串,例如string str;
  • string(const char * s);       //使用字符串s初始化
  • string(const string & str);  //使用一个string对象初始化另一个string对象
  • string(int n,char c);       //使用n个字符c初始化
#include 
using namespace std;
//string的构造函数
void test01()
{
	string s1;
	const char* str = "Hello World";
	string s2(str);
	cout << "s2 = " << s2 << endl;
	string s3(s2);
	cout << "s3 = " << s3 << endl;
	string s4(10, 'a');
	cout << "s4 = " << s4 << endl;
}

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

3.1.3string赋值操作

功能描述:给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赋给当前字符串

示例:

#include 
using namespace std;

void test01()
{
	string str1;
	str1 = "Hello World";
	cout << "str1 = " << str1 << endl;
	string str2 = str1;
	cout << "str2 = " << str2 << endl;
	string str3;
	str3= 'a';
	cout << "str3 = " << str3 << endl;
	string str4;
	str4.assign("Hello C++");
	cout << "str4 = " << str4 << endl;
	string str5;
	str5.assign("Hello C++", 5);
	cout << "str5 = " << str5 << endl;
	string str6;
	str6.assign(str5);
	cout << "str6 = " << str6 << endl;
	string str7;
	str7.assign(10, 'w');
	cout << "str7 = " << str7 << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

3.1.4string字符串拼接

功能描述:实现在字符串末尾拼接字符串

函数原型:

  • string & operator+=(const char * str);           //重载+=运算符
  • string & operator+=(const char c);       //重载+=运算符
  • string & operator+=(const string & str);      //重载+=运算符
  • string & append(const char * s);        //把s字符串连接到当前字符串结尾
  • string & append(const char * s,int n);     //把字符串s的前n个字符连接到当前字符串的结尾
  • string & append(const string &s);          //同operator+=(const string & str)
  • string & append(const string &s,int pos,int n); //字符串s中从pos开始的n个字符连接到字符串末尾

示例:

#include 
using namespace std;

void test01()
{
	string str1 = "我";
	str1 += "爱玩游戏";
	cout << "str1 = " << str1 << endl;
	str1 += ":";
	cout << "str1 = " << str1 << endl;
	string str2 = " LOL DNF";
	str1 += str2;
	cout << "str1 = " << str1 << endl;
	string str3 = "I";
	str3.append(" Love");
	cout << "str3 = " << str3<< endl;
	str3.append(" Game abcde", 5);
	cout << "str3 = " << str3 << endl;
	str3.append(str2);
	cout << "str3 = " << str3 << endl;
	str3.append(str2, 0, 4); //参数2从那个位置截取 参数3是要截取的参数个数
	cout << "str3 = " << str3 << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

3.1.5string查找和替换

功能描述:

  • 查找:查找指定字符串是否存在
  • 替换:在指定位置替换字符串

函数原型:

  • int find(const string &str,int pos = 0)const;     //查找str中第一次出现位置,从pos开始查找
  • int find(const char *s,int pos = 0)const;       //查找s第一次出现位置,从pos开始查找
  • int find(const char * s,int pos,int n)const;     //从pos位置查找s的前n个字符第一次位置
  • int find(const char c,int pos = 0)const;          //查找字符c第一次出现位置
  • int rfind(const string & str,int pos = npos);      //查找str最后一次位置,从pos开始查找
  • int rfind(const char *s,int pos = npos);        //查找s 最后一次出现位置,从pos开始查找
  • int rfind(const char * s,int pos,int n)const;     //从pos位置查找s的前n个字符最后一次位置
  • int rfind(const char c,int pos = 0)const;          //查找字符c最后一次出现位置
  • string& replace(int pos,int n,const string & str);     //替换从pos开始n个字符为字符串str
  • string& replace(int pos,int n,const char *s);    //替换从pos开始的n个字符为字符串s

示例:

#include 
using namespace std;
//字符串的查找和替换
//1.查找
void test01()
{
	string str1 = "abcdefg";
	int pos = str1.find("de");
	if (pos == -1)
	{
		cout<<"未找到字符串!"<

总结:

  • find是从左往右,rfind是从右往左
  • find找到字符串后返回查找的第一个字符位置,找不返回-1
  • replace在替换时,要制定总那个位置起,多少个字符,替换成什么样的字符串

3.1.6string字符串比较

功能描述:字符串之间比较

比较方式:

  • 字符串是按字符的ASCII码进行对比
    • = 返回 0
    • > 返回 1
    • < 返回 -1

函数原型:

  • int compare(const string & s)const;    //与字符串s比较
  • int compare(const char *s)const;     //与字符串s比较
#include 
using namespace std;
//字符串比较
void test01()
{
	string str1 = "xello";
	string str2 = "hello";
	if (str1.compare(str2) == 0)
	{
		cout << "str1 = str2" << endl;
	}
	else if (str1.compare(str2) > 0)
	{
		cout << "str1 > str2" << endl;
	}
	else
	{
		cout << "str1 < str2" << endl;
	}
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:字符串比较主要用于判断两个字符串是不是想等,比较谁打谁小意义不大。

3.1.7string字符提取

string中单个字符的提取有两种方式

  • char & operator[](int n);               //通过[]方式取字符
  • char & at(int n);                           //通过at方法获取字符
#include 
using namespace std;

//string字符提取
void test01()
{
	string str = "hello";
	cout << "str = " << str << endl;
	//1.通过[]访问单个字符
	for (int i = 0; i < str.size(); i++)
	{
		cout << str[i] << " ";
	}
	cout << endl;
	//2.通过at方式访问单个字符
	for (int i = 0; i < str.size(); i++)
	{
		cout << str.at(i) << " ";
	}
	cout << endl;
	//修改单个字符
	str[0] = 'x';
	cout << "str = " << str << endl;
	str.at(1) = 'x';
	cout << "str = " << str << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.1.8string插入和删除

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

函数原型:

  • string & insert(int pos,const char * s);                   //插入字符串
  • string & insert(int pos,const string & str);             //插入字符串
  • string & insert(int pos,int n,char c);             //在指定位置插入n个字符c
  • string & erase(int pos,int n = npos);          //删除从pos开始的n个字符
//string& insert(int pos, const char* s);                   //插入字符串
//string& insert(int pos, const string& str);             //插入字符串
//string& insert(int pos, int n, char c);             //在指定位置插入n个字符c
//string& erase(int pos, int n = npos);          //删除从pos开始的n个字符
#include 
using namespace std;
void test01()
{
	string str = "hello";
	//插入
	str.insert(1, "111");
	cout << "str = " << str << endl;
	//删除
	str.erase(1, 3);
	cout << "str = " << str << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

总结:插入和删除起始下标都是从0开始

3.1.9string子串

功能描述:从字符串中获取想要的子串

函数原型:string substr(iny pos = 0,int n = npos)const; //返回由pos开始的n个字符组成的字符串

示例:

#include 
using namespace std;
//string求子串
void test01()
{
	string str = "abcdef";
	string subStr = str.substr(1, 3);
	cout << "subStr = " << subStr << endl;
}
//实用操作
void test02()
{
	string email = "[email protected]";
	//从邮件中获取用户名信息
	int pos = email.find("@");
	string userName = email.substr(0, pos);
	cout << "userName = " << userName << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

总结:灵活的运用求子串功能,可以在实际开发中获取有效信息。

3.2vector容器

3.2.1vector基本概念

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

vector与普通数组区别:不同之处在于数组是静态空间,而vector可以动态扩展

动态扩展:

  • 并不是在原空间之后接续新空间,而是寻找更大的内存空间,然后将原数据拷贝新空间,释放原空间。C++提高编程(泛型编程与STL技术)_第2张图片
  • vector容器的迭代器是支持随机访问的迭代器 

3.2.2vector构造函数

功能描述:创建vector容器

函数原型:

  • vector v;            //采用模板实现类实现,默认构造函数
  • vector(v.begin(),v.end());      //将v.begin(),end()区间中的元素拷贝给本身
  • vector(n,elem);                 //构造函数将n个elem拷贝给本身
  • vector     //拷贝构造函数

示例:

#include 
using namespace std;
#include 
void printVector(vector &v)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
//vector容器的构造
void test01()
{
	vector v1;  //默认构造 无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);
	//通过区间方式构造
	vectorv2(v1.begin(), v1.end());
	printVector(v2);
	//n个elem方式构造
	vector v3(10, 100);
	printVector(v3);
	//拷贝构造
	vector v4(v3);
	printVector(v4);
}
int main()
{
	test01();
	system("pause");
	return  0;
}

3.2.3vector赋值操作

功能描述:给vector容器进行赋值

函数原型:

  • vector& operator=(const vector &vec);          //重载等号操作符
  • assign(beg,end);          //将[beg,end)区间中的数据拷贝复制给本身
  • assign(n,elem)           //将elem拷贝赋值给本身

示例:

#include 
#include 
using namespace std;
void printVector(vector &v)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
//vector的赋值
void test01()
{
	vector v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);
	//赋值 operator=
	vectorv2 = v1;
	printVector(v2);
	//assign
	vector v3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);
	//assign n个elem
	vector v4;
	v4.assign(10, 100);
	printVector(v4);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.2.4vector容量和大小

功能描述:对vector容器的容量和大小操作

函数原型:

  • empty();            //判断容器是否为空
  • capacity();      //容器的容量
  • size();                        //返回容器中元素的个数
  • resize(int num);      //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。若容器变短,则末尾超出容器长度的元素被删除。
  • resize(int num,elem);   // 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。若容器变短,则末尾超出容器长度的元素被删除。
#include 
#include 
using namespace std;
void printVector(vector& v)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
//vector容器容量和大小
void test01()
{
	vector v1;
	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的大小为:" << v1.size() << endl;
	}
	//重新指定大小
	v1.resize(15,100); //利用重载版本,可以指定默认填充值,参数2
	printVector(v1);
	v1.resize(16);
	printVector(v1);  //如果重新指定过长,默认用0填充新的位置
	v1.resize(5);
	printVector(v1); //如果重新指定的比原来的短了,超出部分会删掉
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.2.5vector插入和删除

功能描述:对vector容器进行插入和删除操作

函数原型:

  • push_back(ele);         //尾部插入元素ele
  • pop_back();          //删除最后一个元素
  • insert(const_iterator pos,ele);        //迭代器指向位置pos插入元素ele
  • insert(const_iterator pos,int count,ele);  //迭代器指向位置pos插入count个元素ele
  • erase(const_iterator pos);          //删除迭代器指向的元素
  • erase(const_iterator start,const_iterator end);          //删除迭代器从start到end之间的元素
  • clear();           //删除容器中所有元素

示例:

#include
#include
using namespace std;
//vector插入和删除
//push_back(ele);         //尾部插入元素ele
//pop_back();          //删除最后一个元素
//insert(const_iterator pos, ele);        //迭代器指向位置pos插入元素ele
//insert(const_iterator pos, int count, ele);  //迭代器指向位置pos插入count个元素ele
//erase(const_iterator pos);          //删除迭代器指向的元素
//erase(const_iterator start, const_iterator end);          //删除迭代器从start到end之间的元素
//clear();           //删除容器中所有元素
void printVector(vector &v)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	vector v1;
	//尾插
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	//遍历
	printVector(v1);
	//尾删
	v1.pop_back();
	printVector(v1);
	//插入 第一个参数是迭代器
	v1.insert(v1.begin(), 100);
	printVector(v1);
	
	v1.insert(v1.begin(), 2, 1000);
	printVector(v1);
	//删除
	v1.erase(v1.begin());
	printVector(v1);

	//v1.erase(v1.begin(), v1.end()); //类似于clear()
	v1.clear();
	printVector(v1);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:

  • 尾插——push_back
  • 尾删——pop_back
  •  插入——insert(位置迭代器)
  • 删除——erase(位置迭代器)
  • 清空——clear

3.2.6vector数据存取

功能描述:对vector中的数据的存取操作

函数原型:

  • at(int idx);            //返回索引idx所指的数据
  • operator[];          //返回索引idx所指的数据
  • front();        //返回容器中第一个数据元素
  • back();      //返回容器中最后一个数据元素
#include 
#include 
using namespace std;

//vector容器——数据存取
void test01()
{
	vector v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	//利用[]方式访问数组元素
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;
	//利用at方式访问元素
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1.at(i) << " ";
	}
	cout << endl;
	//获取第一个元素
	cout << "第一个元素为:" << v1.front() << endl;
	//获取最后一个元素
	cout << "最后一个元素为:" << v1.back() << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.2.7vector互换容器

功能描述:实现两个容器内元素进行互换

函数原型:

  • swap(vec);                       //将vec与本身的元素互换

示例:

#include 
#include 
using namespace std;
//vector容器互换
void printVector(vector &v)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
//1.基本使用
void test01()
{
	cout << "交换前:" << endl;
	vector v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);
	vector v2;
	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()
{
	vectorv;
	for (int i = 0; i < 100000; i++)
	{
		v.push_back(i);
	}
	cout << "v的容量为:" << v.capacity() << endl;
	cout << "v的大小为:" << v.size() << endl;
	v.resize(3);
	cout << "v的容量为:" << v.capacity() << endl;
	cout << "v的大小为:" << v.size() << endl;
	//巧用swap收缩内存
	vector(v).swap(v);
	cout << "v的容量为:" << v.capacity() << endl;
	cout << "v的大小为:" << v.size() << endl;
}
int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

 总结:swap可以使两个容器互换,可以达到实用的收缩内存效果。

笔记:

C++提高编程(泛型编程与STL技术)_第3张图片

vector(v) 是匿名对象,匿名对象调用拷贝构造函数,它利用v现有的实际大小初始化,所以这个匿名对象的容量为3,交换之后匿名对象指向的大容量空间系统会回收,因为执行完这行代码之后系统发现是匿名对象会调用它的析构函数回收掉。

C++提高编程(泛型编程与STL技术)_第4张图片

 3.2.8vector预留空间

功能描述:减少vector在动态扩展容量时的扩展次数

函数原型:

  • reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问

示例:

#include 
#include 
using namespace std;
//vector容器预留空间
void test01()
{
	vector v;
	//利用resreve预留空间
	v.reserve(100000);
	int num = 0;        //统计开辟次数
	int* p = NULL;
	for (int i = 0; i < 100000; i++)
	{
		v.push_back(i);
		if(p != &v[0])
		{
			p = &v[0];
			num++;
		}
	}
	cout << "num = " << num << endl;

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

总结:如果数据量较大,可以一开始就用reserve预留空间。

3.3deque容器

3.3.1deque容器基本概念

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

deque与vector区别:

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

C++提高编程(泛型编程与STL技术)_第5张图片

 deque内部工作原理:

deque内部有个中控器,维护每段缓冲区的内容,缓冲区存放真实数据

中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间

C++提高编程(泛型编程与STL技术)_第6张图片

 deque容器的迭代器也是支持随机访问的。

3.2.2deque构造函数

功能描述:deque容器构造

函数原型:

  • deque deqT;               //默认构造形式
  • deque;           //构造函数将[beg,end)区间中的元素拷贝给本身
  • deque(n,elem);         //构造函数将n个elem拷贝给本身
  • deque(const deque &deq)  //拷贝构造函数

示例:

#include 
#include 
using namespace std;
//deque容器构造函数
void printDeque(const deque& d)
{
	for (deque::const_iterator it = d.begin(); it != d.end(); it++)
	{
		//*it = 100;   //限定为只读状态,容器里的数据不可以修改了 
		cout << *it << " ";
	}
	cout < d1;
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);
	deque d2(d1.begin(), d1.end());
	printDeque(d2);
	dequed3(10, 100);
	printDeque(d3);
	deque d4(d3);
	printDeque(d4);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.3.3deque赋值操作

功能描述:给deque容器进行赋值

函数原型:

  • deque& operator=(const deque &deq);        //重载等号操作
  • assign(beg,end);              //将[beg,end)区间中的数据拷贝赋值给本身
  • assign(n,elem);             //将n个elem拷贝赋值给本身

示例:

#include
#include  
using namespace std;
//deque容器赋值操作
void printDeque(const deque &d)
{
	for (deque::const_iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	deque d1;
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);
	deque d2 = d1;
	printDeque(d2);
	deque d3(d1.begin(), d1.end());
	printDeque(d3);
	dequed4(10, 100);
	printDeque(d4);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.3.4deque大小操作

功能描述:对deque容器的大小进行操作

函数原型:

  • deque.empty();             //判断容器是否为空
  • deque.size();            //返回容器中元素的个数
  • deque.resize(num);      //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
  • deque.resize(num,elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

示例:

#include 
#include 
using namespace std;

//deque容器-大小操作
void printDeque(const deque &d)
{
	for (deque::const_iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	deque d1;
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);
	if (d1.empty())
	{
		cout << "d1为空" << endl;
	}
	else
	{
		cout << "d1不为空!" << endl;
		cout << "d1的大小为:" << d1.size() << endl;
	}
	//重新指定大小
	d1.resize(15);
	printDeque(d1);
	d1.resize(20, 1);
	printDeque(d1);
	d1.resize(5);
	printDeque(d1);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:deque没有容量的概念。他是用中控器搞的。

3.3.5deque插入和删除

功能描述:向deque容器中插入和删除数据

函数原型:

两端操作:

  • push_back(elem);          //在容器尾部添加一个数据
  • push_front(elem);          //在容器头部添加一个数据
  • pop_back();              //删除容器中最后一个数据
  • pop_front();               //删除容器中第一个数据

指定位置操作:

  • insert(pos,elem);              //在pos位置插入一个elem元素的拷贝,返回新数据的位置
  • insert(pos,n,elem);      //在pos位置插入n个elem数据,无返回值
  • insert(pos,beg,end);          //在pos位置插入[beg,end)区间的数据,无返回值
  • clear();                         //清空容器中的所有数据
  • erase(beg,end);         //删除[beg,end)区间的数据,返回下一个数据的位置
  • erase(pos);             //删除pos位置的数据,返回下一个数据的位置
#include 
#include 
using namespace std;
//deque容器的插入和删除
void printDeque(const deque &d)
{
	for (deque::const_iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	dequed1;
	//尾插
	d1.push_back(10);
	d1.push_back(20);
	//头插
	d1.push_front(100);
	d1.push_front(200);
	printDeque(d1);
	//尾删
	d1.pop_back();
	printDeque(d1);
	//头删
	d1.pop_front();
	printDeque(d1);
}
void test02()
{
	deque d1;
	d1.push_back(10);
	d1.push_back(20);
	d1.push_front(100);
	d1.push_front(200);
	printDeque(d1);
	//insert插入
	d1.insert(d1.begin(), 1000);
	printDeque(d1);
	d1.insert(d1.begin(), 2,10000);
	printDeque(d1);
	//按照区间进行插入
	dequed2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);
	d1.insert(d1.begin(), d2.begin(), d2.end());
	printDeque(d1);
}
void test03()
{
	deque d1;
	d1.push_back(10);
	d1.push_back(20);
	d1.push_front(100);
	d1.push_front(200);
	//删除
	deque::iterator it = d1.begin();
	it++;
	d1.erase(it);
	printDeque(d1);
	//按照区间方式删除
	d1.erase(d1.begin(), d1.begin() + 2);
	printDeque(d1);
	d1.clear();
	printDeque(d1);
}
int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

总结:插入和删除提供的位置是迭代器。而非数字。即原型中pos为d.begin()等。

3.3.6deque数据存取

功能描述:对deque中的数据存取操作

函数原型:

  • at(int idx);           // 返回索引idx所指的数据
  • operator[];        //返回索引idx所指的数据
  • front();               //返回容器中第一个数据元素
  • back();           //返回容器中最后一个数据元素
#include 
#include 
using namespace std;
//deque数据存取
void test01()
{
	deque d;
	d.push_back(10);
	d.push_back(20);
	d.push_back(30);
	d.push_front(100);
	d.push_front(200);
	d.push_front(300);

	//通过[]方式访问元素
	for (int i = 0; i < d.size(); i++)
	{
		cout << d[i] << " ";
	}
	cout << endl;
	//通过at方式访问元素
	for (int i = 0; i < d.size(); i++)
	{
		cout << d.at(i)<< " ";
	}
	cout << endl;
	cout << "第一个元素为:" << d.front ()<< endl;
	cout << "最后一个元素为:" << d.back() << endl;

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

总结:除了用迭代器获取deque容器中的元素(遍历,输出*it),[]和at也可以

3.3.7deque排序

功能描述:利用算法实现对deque容器进行排序

算法:

  • sort(iterator beg,iterator end);      //对beg和end区间内元素进行排序

示例:

#include 
#include 
#include     //标准算法头文件
using namespace std;
//deque容器排序
void printDeque(const deque &d)
{
	for (deque::const_iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	deque d;
	d.push_back(10);
	d.push_back(20);
	d.push_back(30);
	d.push_front(100);
	d.push_front(200);
	d.push_front(300);
	printDeque(d);
	//排序  默认排序规则是升序
	//对于支持随机访问的迭代器的容器,都可以利用sort算法直接对其进行排序
	//vector容器也可以利用sort进行排序
	sort(d.begin(), d.end());
	printDeque(d);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.4案例——评委打分

3.4.1案例描述

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

3.4.2实现步骤

  1. 创建五名选手,放到vector中
  2. 遍历vector容器,取出来的每一个选手,执行for循环,可以把10个评分打分放到deque容器中
  3. sort算法对deque容器中分数排序,去除最高分和最低分
  4. deque容器遍历一遍,累加总分
  5. 获取平均分

示例代码:

#include
#include 
#include 
#include 
#include 
using namespace std;

//选手类
class Person
{
public:
	Person(string name, int score)
	{
		m_name = name;
		m_score = score;
	}
	string m_name;
	int m_score;
};
void creatPerson(vector&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& v)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		//将评委分数放入到deque容器中
		deque d;
		for (int i = 0; i < 10; i++)
		{
			int score = rand() % 41 + 60;    //60-100
			d.push_back(score);
		}
		//排序
		sort(d.begin(), d.end());
		//去除最高分和最低分
		d.pop_back();
		d.pop_front();
		//取平均分
		int sum = 0;
		for (deque::iterator dit = d.begin(); dit != d.end(); dit++)
		{
			sum += *dit;
		}
		int avg = sum / d.size();

		//将平均分赋值给选手
		it->m_score = avg;
	}
}
void showScore(vector &v)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it).m_name << "\t分数:" << (*it).m_score << endl;
	}
}
int main()
{
	srand((unsigned int)time(NULL));
	//1.创建五名选手
	vector v;    //存放选手的容器
	creatPerson(v);
	测试
	//for (vector::iterator it = v.begin(); it != v.end(); it++)
	//{
	//	cout << "姓名:" << (*it).m_name << "\t分数:" << (*it).m_score << endl;
	//}
	//2.给五名选手打分
	setScore(v);
	//3.显示最后得分
	showScore(v);
	system("pause");
	return 0;
}

3.5stack容器

3.5.1stack基本概念

概念:stack是一种先进后出(First In Last Out,FILO)的数据结构,他只有一个出口

C++提高编程(泛型编程与STL技术)_第7张图片

 栈中只有顶端元素才能被外界使用,因此栈不允许有遍历行为

C++提高编程(泛型编程与STL技术)_第8张图片

栈中进入数据称为——入栈push

栈中弹出数据称为——出栈pop

3.5.2stack容器常用接口

构造函数:

  • stack stk;        //stack采用模板类实现,stack对象的默认构造形式
  • stack(const stack &stk); //拷贝构造函数

赋值操作:

  • stack& operator=(const stack & stk);  //重载等号操作符

数据存取:

  • push(elem);           //向栈顶添加元素
  • pop();       //从栈顶中移除第一个元素
  • top();            //返回栈顶元素

大小操作:

  • empty();    //判断堆栈是否为空
  • size();       //返回栈的大小

示例:

#include 
#include 
using namespace std;
//stack常用接口
void test01()
{
	//特点:符合先进后出数据结构
	stack s;
	//入栈
	s.push(10);
	s.push(20);
	s.push(30);
	s.push(40);
	//只要栈不为空,查看栈顶并执行出栈操作
	while(!s.empty())
	{
		//查看栈顶元素
		cout << "栈顶元素为:" << s.top() << endl;
		//出栈
		s.pop();
	}
	cout << "栈的大小:" << s.size() << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.6queue容器

3.6.1queue基本概念

概念:Queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口

C++提高编程(泛型编程与STL技术)_第9张图片 队列容器允许从一端新增元素,从另一端移除元素

队列中只有队头和队尾才可以被外界是使用,因此队列不允许有遍历行为

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

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

C++提高编程(泛型编程与STL技术)_第10张图片

3.6.2queue常用接口

功能描述:栈容器常用的对外接口。

构造函数:

  • queue que;       //queue采用模板类实现,queue对象的默认构造形式
  • queue(const queue &que);   //拷贝构造函数

赋值操作:

  • queue& operator=(const queue &que);    //重载等号操作符

数据存取:

  • push(elem);           //往队尾添加元素
  • pop();           //从队头移除第一个元素
  • back();        //返回最后一个元素
  • front();          //返回第一个元素

大小操作:

  • empty();      //判断堆栈是否为空
  • size();       //返回栈的大小

示例:

#include 
#include 
using namespace std;
//队列
class Person
{
public:
	Person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}
	string m_name;
	int m_age;
};
void test01()
{
	queueq;
	//准备数据
	Person p1("唐僧", 30);
	Person p2("孙悟空", 1000);
	Person p3("猪八戒", 900);
	Person p4("沙和尚", 800);
	//入队
	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);
	
	while (!q.empty())
	{
		//查看队头
		cout << "队头元素——姓名:" << q.front().m_name << "\t年龄:" << q.front().m_age << endl;
		cout << "队尾元素——姓名:" << q.back().m_name << "\t年龄:" << q.back().m_age << endl;
		//出队
		q.pop();
	}
	cout << "队列大小:" << q.size() << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.7list容器

3.7.1list基本概念

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

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

链表的组成:链表由一系列结点组成

结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

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

C++提高编程(泛型编程与STL技术)_第11张图片

C++提高编程(泛型编程与STL技术)_第12张图片 由于链表的存储方式并不是连续的内存空间,因此链表中的迭代器只支持前移和后移,属于双向迭代器。

list的优点:

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要大量移动元素

list的缺点:

  • 链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大

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

总结:STL中List和Vector是两个最常被使用的容器,各有优缺点。

 3.7.2list构造函数

功能描述:创建list容器

函数原型:

  • list lst;          //list采用模板类实现,对象的默认构造形式
  • list(beg,end);     //构造函数将[beg,end)区间的元素拷贝给本身
  • list(n,elem);        //构造函数将n个elem拷贝给本身
  • list(const list &lst);        //拷贝构造函数

示例:

#include 
#include
using namespace std;
//list容器的构造
void printList(const list& L)
{
	for (list::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	list L1;
	//添加数据
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	//遍历容器
	printList(L1);
	//区间方式构造
	list L2(L1.begin(), L1.end());
	printList(L2);
	//拷贝构造
	list L3(L2);
	printList(L3);
	//n个elem
	list L4(10, 1000);
	printList(L4);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.7.3list赋值和交换

功能描述:给list容器进行赋值,以及交换list容器

函数原型:

  • assign(beg,end);     //将[brg,end)区间中的数据拷贝赋值给本身
  • assign(n,elem);        //将n个elem拷贝赋值给本身
  • list &operator= (const list&lst);      //重载等号操作符
  • swap(lst);              //将lst与本身的元素互换

示例:

#include
#include 
using namespace std;
//list容器赋值和交换
void printList(const list& L)
{
	for (list::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
//赋值
void test01()
{
	list L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	printList(L1);
	list L2;
	L2 = L1;  //operator=赋值
	printList(L2);
	list L3;
	L3.assign(L2.begin(), L2.end());
	printList(L3);
	list L4;
	L4.assign(10, 100);
	printList(L4);
}
//交换
void test02()
{
	list L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	printList(L1);
	list L2;
	L2.assign(10, 100);
	printList(L2);
	cout << "交换后:" << endl;
	L1.swap(L2);
	printList(L1);
	printList(L2);
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

3.7.3list大小操作

功能描述:对list容器的大小进行操作

函数原型:

  • size();        //返回容器中元素的个数
  • empty();   //判断容器是否为空
  • resize(num);      //重新指定容器长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
  • resize(num,elem);     //重新指定容器长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

示例:

#include
#include 
using namespace std;
//list大小操作
void printList(const list &L)
{
	for (list::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	list 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, 20);
	printList(L1);
	L1.resize(5);
	printList(L1);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.7.5list插入和删除

功能描述:对list容器进行数据的插入和删除

函数原型:

  • push_back(elem);          //在容器尾部插入一个元素
  • pop_back();                //删除容器中最后一个元素
  • push_front(elem);            //在容器头部插入一个元素
  • pop_front();          //从容器开头移除第一个元素
  • insert(pos,elem);     //在pos位置插elem元素的拷贝,返回新数据的位置
  • insert(pos,n,elem);     //在pos位置插n个elem数据,无返回值
  • insert(pos,beg,end);     //在pos位置插[beg,end)区间的数据,无返回值
  • clear();              //移除容器中的所有数据
  • erase(beg,end);    //删除[beg,end)区间的数据,返回下一个数据的位置
  • erase(pos);     //删除pos位置的数据,返回下一个数据的位置
  • remove(elem);   //删除容器中所有与elem值匹配的元素。
#include 
#include 
using namespace std;
//list容器的插入和删除
void printList(const list &L)
{
	for (list::const_iterator it = L.begin(); it != L.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	list 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);
	//删除
	L.erase(++L.begin());
	printList(L);
	//remove移除
	L.push_back(10000);
	printList(L);
	L.remove(10000);
	printList(L);
	L.push_back(10000);
	L.push_back(10000);
	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;
}

3.7.6list数据存取

功能描述:对list容器中的数据进行存取

函数原型:

  • front();                      //返回第一个元素
  • back();            //返回最后一个元素
#include
#include
using namespace std;
//list容器中的数据存取
void test01()
{
	list L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	cout << "第一个元素是:" << L1.front() << endl;
	cout << "最后一个元素是:" << L1.back() << endl;
	//L1[0]    不可以用[]访问list容器中的元素
	//L1.at(0) 不可以用at方式访问list中的元素
	//原因是list本质链表,不是用连续线性空间存储数据,迭代器也不是支持随机访问的
	
	//验证迭代器是不支持随机访问的
	list::iterator it = L1.begin();
	it++;      //支持双向
	it--;
	//it = it + 1;      //不支持随机访问
}
int main()
{
	test01();
	system("pause");
	return 0;
}

tips:

验证是否支持随机访问: it+1;不报错说明支持随机访问,报错说明不支持随机访问。

验证是单向还是双向:如果it++不报错it--报错说明是单向。如果都不报错说明支持双向。

3.7.7list反转和排序

功能描述:将容器中的元素反转,以及将容器中的数据进行排序。

函数原型:

  • reverse();         //反转链表
  • sort();                 //链表排序

示例:

#include 
#include
using namespace std;
//list容器的反转和排序
void printList(const list& L)
{
	for (list::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 L1;
	L1.push_back(20);
	L1.push_back(10);
	L1.push_back(50);
	L1.push_back(40);
	L1.push_back(30);
	cout << "反转前:" << endl;
	printList(L1);
	//反转后
	L1.reverse();
	cout << "反转后:" << endl;
	printList(L1);
}
//排序
void test02()
{
	list L1;
	L1.push_back(20);
	L1.push_back(10);
	L1.push_back(50);
	L1.push_back(40);
	L1.push_back(30);
	cout << "排序前:" << endl;
	printList(L1);
	//排序后
	//sort(L1.begin(), L1.end());//所有不支持随机访问的迭代器的容器,不可以用标准算法
	//不支持随机访问迭代器的容器,内部会提供一些对应的算法,没法用全局函数,用成员函数即可
	L1.sort();//默认排序规则是从小到大
	cout << "排序后:" << endl;
	printList(L1);
	L1.sort(Compare);
	printList(L1);
}

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

3.7.8排序案例

案例描述:将Person自定义数据类型进行排序,Person中有姓名、年龄、身高

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

示例:

#include
#include 
using namespace std;
//list容器排序案例
class Person
{
public:
	Person(string name, int age, double height)
	{
		m_age = age;
		m_name = name;
		m_height = height;
	}
	string m_name;
	int m_age;
	double m_height;
};
//指定排序规则
bool Compare(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 test01()
{
	listL;  //创建容器
	//准备数据
	Person p1("刘备", 35, 175.2);
	Person p2("曹操", 45, 180.1);
	Person p3("孙权", 40, 175.1);
	Person p4("赵云", 25, 190.2);
	Person p5("张飞", 35, 160.1);
	Person p6("关羽", 35, 200.3);
	//插入数据
	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::iterator it = L.begin(); it != L.end(); it++)
	{
		cout << "姓名:" << (*it).m_name << "\t年龄:" << it->m_age << "身高:" << it->m_height << endl;
	}
	//排序
	cout << "------------------------------------------------------" << endl;
	cout << "排序后:" << endl;
	L.sort(Compare);
	for (list::iterator it = L.begin(); it != L.end(); it++)
	{
		cout << "姓名:" << (*it).m_name << "\t年龄:" << it->m_age << "身高:" << it->m_height << endl;
	}

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

注意高级排序写法。

总结:

  • 对于自定义数据类型,必须指定排序规则,否则编译器不知道如何进行排序
  • 高级排序只是在排序规则上再进行一次逻辑规则制定,并不复杂

3.8set/multiset容器

3.8.1set概念

简介:所有元素都会在插入时被自动排序

本质:set和multiset属于关联式容器,底层结构是用二叉树实现。

set和multiset区别:

  • set不允许容器中有重复元素
  • multiset允许容器中有重复的元素

3.8.2set的构造和赋值

功能描述:创建set容器以及赋值

构造:

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

赋值:

  • set& operator=(const set &st);  //重载等号操作符

示例:

#include 
#include 
using namespace std;
void printSet(set &s)
{
	for (set::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	set s1;
	//插入数据只有insert方式
	s1.insert(10);
	s1.insert(40);
	s1.insert(30);
	s1.insert(20);
	s1.insert(30);
	//遍历容器
	//set容器特点:所有元素在插入的时候自动被排序
	//set容器不允许插入重复值
	printSet(s1);
	//拷贝构造
	set s2(s1);
	printSet(s2);
	//赋值操作
	set s3;
	s3 = s2;
	printSet(s3);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.8.3set大小和交换

功能描述: 统计set容器大小以及交换set容器。

函数原型:

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

示例:

#include 
#include
using namespace std;
//set容器大小和交换
//大小
void printSet(set& s)
{
	for (set::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	set s1;
	s1.insert(10);
	s1.insert(20);
	s1.insert(30);
	s1.insert(40);
	printSet(s1);
	
	if (s1.empty())
	{
		cout << "s1为空" << endl;
	}
	else
	{
		cout << "s1不为空" << endl;
		cout << "s1的大小为:" << s1.size() << endl;
	}
}
//交换
void test02()
{
	set s1;
	s1.insert(10);
	s1.insert(20);
	s1.insert(30);
	s1.insert(40);
	set s2;
	s2.insert(100);
	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();
	test02();
	system("pause");
	return 0;
}

3.8.4set插入和删除

功能描述:set容器进行插入和删除数据

函数原型:

  • insert(elem);       //在容器中插入元素
  • clear();         //清除所有元素
  • erase(pos);  //删除pos迭代器所指的元素,返回下一个元素的迭代器
  • erase(beg,end); //删除区间[beg,end)区间的所有元素,返回下一个元素的迭代器
  • erase(elem); //删除容器中值为elem的元素

示例:

#include
#include
using namespace std;
//set容器的插入和删除
void printSet(set& s)
{
	for (set::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test01()
{
	set s1;
	s1.insert(10);
	s1.insert(20);
	s1.insert(30);
	s1.insert(40);
	printSet(s1);
	//删除
	s1.erase(s1.begin());
	printSet(s1);
	//删除重载版本
	s1.erase(30);
	printSet(s1);
	s1.erase(s1.begin(), s1.end());
	printSet(s1);

}
int main()
{
	test01();

	system("pause");
	return 0;
}

3.8.5set查找和统计

功能描述:对set容器进行查找数据以及统计数据

函数原型:

  • find(key);    //查找key是否存在,若存在,返回改键的元素的迭代器;若不存在,返回set.end();
  • count(key); //统计key元素的个数

示例:

#include 
#include 
using namespace std;
//set容器-查找和统计
void test01()
{
	set s1;
	s1.insert(10);
	s1.insert(20);
	s1.insert(30);
	s1.insert(40);
	//查找
	set::iterator pos = s1.find(30);
	if (pos != s1.end())
	{
		cout << "找到元素:" << *pos << endl;
	}
	else
	{
		cout << "未找到元素!" < s1;
	s1.insert(10);
	s1.insert(20);
	s1.insert(30);
	s1.insert(30);
	s1.insert(30);
	s1.insert(30);
	s1.insert(40);
	//统计30的个数
	int num = s1.count(30);//对于set而言,统计的结果要么是0要么是1
	cout << "number = " << num << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

3.8.6set和multiset区别

区别:

  • set不可以插入重复数据,而multiset可以
  • set插入数据的同时会返回插入结果,表示插入是否成功
  • multiset不会检测数据,因此可以插入重复数据

示例:

#include 
#include 
using namespace std;
//set容器和multiset容器区别
void test01()
{
	set s;
	pair::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 ms;
	//允许插入重复的值
	ms.insert(10);
	ms.insert(10);
	for (multiset::iterator it = ms.begin(); it != ms.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:

  • 如果不允许插入重复数据,可以利用set
  • 如果需要插入重复数据利用multiset

3.8.7pair队组创建

功能描述:成对出现的数据,利用对组可以返回两个数据

两种创建方式:

  • pair p(value1,value2);
  • pair p = make_pair(value1,value2);

示例:

#include 
using namespace std;
//pair对组的创建
void test01()
{
	//第一种方式
	pairp("Tom", 20);
	cout << "姓名:" << p.first << "\t年龄:" << p.second << endl;
	//第二种方式
	pairp2 = make_pair("Jerry", 30);
	cout << "姓名:" << p2.first << "\t年龄:" << p2.second << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.8.8set容器排序

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

示例一:set存放内置数据类型

#include 
#include 
using namespace std;
//set容器排序
class myCompare
{
public:
	bool operator()(int v1,int v2)const
	{
		return v1 > v2;
	}
};
void test01()
{
	set s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(50);
	s1.insert(30);
	for (set::iterator it = s1.begin(); it != s1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	//指定排序规则为从大到小
	set s2; //仿函数
	s2.insert(10);
	s2.insert(40);
	s2.insert(20);
	s2.insert(50);
	s2.insert(30);
	for (set::iterator it = s2.begin(); it != s2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:利用仿函数可以指定set容器的排序规则。

示例二:set存放自定义数据类型

#include 
#include 
using namespace std;
//set容器排序,存放自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}
	string m_name;
	int m_age;
};
class compare
{
public:
	bool operator()(const Person  &p1,const Person &p2)const
	{
		return p1.m_age > p2.m_age;
	}
};
void test01()
{
	set s;
	Person p1("刘备", 24);
	Person p2("关羽", 28);
	Person p3("张飞", 25);
	Person p4("赵云", 21);
	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);
	for (set::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << "姓名:" << it->m_name << "\t年龄:" << it->m_age << endl;
	}
	cout << endl;
	
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 总结:对于自定义数据类型,set必须指定排序规则才可以插入数据。

3.9map/multimap容器

3.9.1map基本概念

简介:

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

本质:map/multimap属于关联式容器,底层结构是用二叉树实现。

优点:可以根据key值快速找到value值。

map和multimap区别:

  • map不允许容器中有重复key值元素
  • multimap允许容器中有重复key值元素

3.9.2map构造和赋值

功能描述:对map容器进行构造和赋值

函数原型:

构造:

  • map mp;    //map默认构造函数
  • map(const map &map);   //拷贝构造函数

赋值:

  • map& operator=(const map & map);   //重载等号操作符
#include 
#include
using namespace std;
//map容器构造和复制
void printMap(map &m)
{
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << "\tvalue = " << it->second << endl;
		cout << endl;
	}
	cout << endl;
}
void test01()
{
	//创建map容器
	map m;
	m.insert(pair(1, 10));
	m.insert(pair(2, 20));
	m.insert(pair(3, 30));
	m.insert(pair(4, 40));
	printMap(m);
	//拷贝构造
	mapm2(m);
	printMap(m2);
	//赋值操作
	map m3 = m2;
	printMap(m3);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:map中所有元素都是成对出现,插入数据要使用对组

3.9.3map大小和交换

功能描述:统计map容器大小以及交换map容器

函数原型:

  • size();     //返回容器中元素数目
  • empty();   //判断容器是否为空
  • swap(st);   //交换两个集合容器

示例:

#include 
#include 
using namespace std;
//map容器大小和交换
void printMap(map &m)
{
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << "\tvalue = " << it->second << endl;
	}
	cout << endl;
}
void test01()
{
	mapm;
	m.insert(pair(1, 10));
	m.insert(pair(2, 20));
	m.insert(pair(3, 30));
	if (m.empty())
	{
		cout << "m为空!" << endl;
	}
	else
	{
		cout << "m不为空!" << endl;
		cout << "m的大小为:" << m.size() << endl;
	}
}
void test02()
{
	mapm1;
	m1.insert(pair(1, 10));
	m1.insert(pair(2, 20));
	m1.insert(pair(3, 30));
	mapm2;
	m2.insert(pair(4, 100));
	m2.insert(pair(5, 200));
	m2.insert(pair(6, 300));
	cout << "交换前:" << endl;
	printMap(m1);
	printMap(m2);
	cout << "交换后:" << endl;
	m1.swap(m2);
	printMap(m1);
	printMap(m2);
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

3.9.4map容器-插入和删除

函数原型:

  • insert(elem);   //在容器中插入元素
  • clear();    //清除所有元素
  • erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
  • erase(beg,end);  //删除区间[beg,end)区间的所有元素,返回下一个元素的迭代器
  • erase(key);  // 删除 容器中值为key的元素

示例:

#include
#include
using namespace std;
//map插入和删除
void printMap(map &m)
{
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << "\tvalue = " << it->second << endl;
	}
	cout << endl;
}
void test01()
{
	map m;
	//插入
	//第一种
	m.insert(pair(1, 10));
	//第二种
	m.insert(make_pair(2, 20));
	//第三种
	m.insert(map::value_type(3, 30));
	//第四种
	//[]不建议插入 用途:可以利用key访问到value
	m[4] = 40;
	printMap(m);
	//删除
	m.erase(m.begin());
	printMap(m);

	m.erase(3);   //按key删除
	printMap(m);

	m.erase(m.begin(), m.end());
	printMap(m);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3.9.5map查找和统计

功能描述:对map容器进行查找数据以及统计数据。

函数原型:

  • find(key);            //查找key是否存在,返回该键的元素的迭代器;若不存在,返回set.end();
  • count(key);         //统计key元素的个数

示例:

#include 
#include 
using namespace std;
//查找和统计
void test01()
{
	//查找
	map m;
	m.insert(pair(1, 10));
	m.insert(pair(2, 20));
	m.insert(pair(3, 30));
	map::iterator pos = m.find(3);
	if (pos != m.end())
	{
		cout << "查到了元素!key = "<first<<"\tvalue = "<second << endl;
	}
	else
	{
		cout << "未找到元素!" << endl;
	}
	//统计
	//map不允许插入重复key元素,count统计而言,结果要么是0要么是1
	//multimap的count统计可能大于1
	int num = m.count(3);
	cout << "num = " <

总结:

  • 查找——find(返回的是迭代器)
  • 统计——count( 对于map,结果为0或者1)

3.9.6map容器排序

学习目标:map容器默认排序规则为按key值进行从小到大排序,掌握如何改变排序规则

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

示例:

#include 
#include 
using namespace std;
//map容器排序
class Compare
{
public:
	bool operator()(int v1, int v2)const
	{
		return v1 > v2;
	}
};
void test01()
{
	map m;
	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 70));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(4, 60));
	m.insert(make_pair(5, 50));
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << "\tvalue = " << it->second << endl;
	}

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

总结:

  • 利用仿函数可以指定map容器的排序规则
  • 对于自定义类型,map必须要指定排序规则,同set容器

3.10案例-员工分组

3.10.1案例描述

  • 公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司后,需要指派员工在那个部门工作
  • 员工信息有:姓名 工资组成; 部门分为:策划 美术 研发
  • 随机给员工分配部门和工资
  • 通过multimap进行信息的插入 key(部门编号)value(员工)
  • 分部门显示员工信息

3.10.2实现步骤:

  1. 创建10名员工,放到vector中
  2. 遍历vector容器,取出每个员工,进行随机分组
  3. 分组后,将员工部门编号作为key,具体员工作为value,放入到multimap容器中
  4. 分部门显示员工信息

案例代码:

#include 
#include 
#include
#include  
using namespace std;
#define CEHUA 0
#define MEISHU 1
#define YANFA 2
//公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司后,需要指派员工在那个部门工作
//员工信息有:姓名 工资组成; 部门分为:策划 美术 研发
//随机给员工分配部门和工资
//通过multimap进行信息的插入 key(部门编号)value(员工)
//分部门显示员工信息
class Worker
{
public:
	string m_name;
	int m_salary;
};
void creatWorker(vector &v)
{
	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;//10000-19999
		//将员工放到容器中
		v.push_back(worker);
	}
}
void setGroup(vector&v, multimap&m)
{
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		//产生随机部门编号
		int deptId = rand() % 3;
		//将员工插入到分组中
		m.insert(make_pair(deptId, *it));
	}
}
void showWorkerByGroup(multimap& m)
{
	cout << "策划部门:" << endl;
	multimap::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 << "\t工资:" << pos->second.m_salary << 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 << "\t工资:" << pos->second.m_salary << 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 << "\t工资:" << pos->second.m_salary << endl;
	}
}
int main()
{
	srand((unsigned int)time(NULL));
	//1.创建员工
	vectorvWorker;
	creatWorker(vWorker);
	测试
	//for (vector::iterator it = vWorker.begin(); it != vWorker.end(); it++)
	//{
	//	cout << "姓名:" << it->m_name << "\t工资:" << it->m_salary << endl;
	//}
	//2.员工分组
	multimapmWorker;
	setGroup(vWorker, mWorker);
	//3.分组显示员工
	showWorkerByGroup(mWorker);
	system("pause");
	return 0;
}

4 STL函数对象

4.1函数对象

4.1.1函数对象概念

概念:

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

本质:

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

4.1.2函数对象使用

特点:

  • 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
  • 函数对象超出普通函数的概念,函数对象可以有自己的状态
  • 函数对象可以作为参数传递
#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()
	{
		count = 0;
	}
	void operator()(string test)
	{
		cout << test << endl;
		count++;
	}
	int count;
};
void test02()
{
	MyPrint myprint;
	myprint("hello world!");
	myprint("hello world!");
	myprint("hello world!");
	myprint("hello world!");
	myprint("hello world!");
	cout << "MyPrint调用次数为:" <

4.2谓词

4.2.1谓词概念

概念:

  • 返回bool类型的仿函数称为谓词;
  • 如果operator()接受一个参数,那么叫做一元谓词
  • 如果operator()接受两个参数,那么叫做二元谓词

4.2.2一元谓词

示例:

#include 
#include
#include
using namespace std;
//一元谓词
//仿函数返回值类型是bool数据类型,称为谓词
class GreaterFive
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}

};
void test01()
{
	vector v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	//查找容器中 有没有大于5的数字
	vector::iterator it = find_if(v.begin(),v.end(),GreaterFive());//GreaterFive()匿名函数对象
	if (it == v.end())
	{
		cout << "未找到" << endl;
	}
	else
	{
		cout << "找到了大于5的数字为:" << *it << endl;
	}
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.2.3二元谓词

示例:

#include
#include 
#include
using namespace std;
//二元谓词
class myCompare
{
public:
	bool operator()(int val1,int val2)
	{
		return val1 > val2;
	}
};
void test01()
{
	vector v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(20);
	v.push_back(30);
	v.push_back(50);
	sort(v.begin(), v.end());
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	//使用函数对象改变算法策略 变为排序规则为降序
	sort(v.begin(), v.end(), myCompare()); //myCompare()匿名函数对象
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.3内建函数对象

4.3.1内建函数对象意义

概念:STL内建了一些函数对象

分类:

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

用法:

  • 这些函数所产生的对象,用法和一般函数完全不同
  • 使用内建函数对象,需要引入头文件#include

4.3.2算数仿函数

功能描述:

  • 实现四则运算
  • 其中negate是一元运算,其他都是二元运算

仿函数原型:

  • templateT plus;       //加法仿函数
  • templateT minus;   //减法仿函数
  • template T multiplies; //乘法仿函数
  • templateT divides;  //除法仿函数
  • templateT modulus; //取模仿函数
  • templateT negate; //取反仿函数

示例:

#include
#include //内建函数对象头文件
using namespace std;
//内建函数对象  算数仿函数
//negate 一元仿函数 取反仿函数
void test01()
{
	negate n;
	cout << n(50) << endl;
}
//plus 二元仿函数 加法运算
void test02()
{
	plusp;
	cout << p(10, 10) << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

总结:使用内建函数对象时,需要引入头文件#include

4.3.3关系仿函数

功能描述:实现关系对比

仿函数原型:

  • template bool equal_to;     //等于
  • templatebool not_equal_to; // 不等于
  • templatebool greater;  //大于
  • templatebool greater_equal;//大于等于
  • templatebool less;     //小于
  • templatebool less_equal;   //小于等于

示例:

#include 
#include 
#include 
#include 
using namespace std;
//内建函数对象-关系仿函数
//大于-greater
class myCompare
{
public:
	bool operator()(int v1,int v2)
	{
		return v1 > v2;
	}
};
void test01()
{
	vectorv;
	v.push_back(10);
	v.push_back(30);
	v.push_back(40);
	v.push_back(20);
	v.push_back(50);
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	//降序
	//sort(v.begin(), v.end(), myCompare());
	//greater()内建函数对象
	sort(v.begin(), v.end(), greater());
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:关系仿函数中最常用的就是greater<>大于。

4.3.4逻辑仿函数

功能描述:实现逻辑运算

函数原型:

  • template bool logical_and;           //逻辑与
  • templatebool logical_or;             //逻辑或
  • templatebool logical_not;         //逻辑非

示例:

#include 
#include 
#include 
#include
using namespace std;
//内建函数对象-逻辑仿函数
//逻辑非-logical_not
void test01()
{
	vector v;
	v.push_back(true);
	v.push_back(false);
	v.push_back(true);
	v.push_back(false);
	for (vector::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	//利用逻辑非 将容器v搬运到v2中,并执行取反操作
	vector v2;
	v2.resize(v.size());  //先开辟空间才能搬运
	transform(v.begin(), v.end(), v2.begin(), logical_not());
	for (vector::iterator it = v2.begin(); it != v2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:逻辑仿函数应用较少,了解即可。

5STL—常用算法

概述:

  • 算法主要由头文件组成。
  • 是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等。
  • 体积很小,只包括几个在序列上面简单数学运算的模板函数
  • 定义了一些模板类,用以声明函数对象

5.1常用遍历算法

算法简介:

  • for_each;       //遍历容器
  • transform;        //搬运容器到另一个容器中

5.1.1for_each

#include 
#include
#include 
using namespace std;
//常用遍历算法 for_each
//普通函数
void print01(int val)
{
	cout << val << " ";
}
//仿函数
class print02 
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
	
};
void test01()
{
	vectorv;
	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());    //对于仿函数来说,第三个参数放函数对象
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:for_each在实际开发中是最常用的遍历算法,需要熟练掌握。

5.1.2transform

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

函数原型:

  • transform(iterator beg1,iterator end1,iterator beg2,_func);      _func函数或函数对象

示例:

#include 
#include 
#include 
using namespace std;
//常用的遍历算法 transform
class Transform
{
public:
	int operator()(int v)
	{
		return v+100;
	}
};
class MyPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
void test01()
{
	vector v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector vTarget;     
	vTarget.resize(v.size());   //目标容器 需要提前开辟空间
	transform(v.begin(), v.end(), vTarget.begin(), Transform());
	for_each(vTarget.begin(), vTarget.end(), MyPrint());
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:搬运的目标容器必须要提前开辟空间,否则无法正常搬运

5.2常用查找算法

算法简介:

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

5.2.1find

功能描述:查找指定元素,找到返回指定元素的迭代器,找不到返回结束迭代器end()

函数原型:find(iterator beg,iterator end,value);//按值查找元素,找到返回指定迭代器,找不到返回结束迭代器

//beg 开始迭代器

//end 结束迭代器

//value 查找的元素

示例:

#include 
#include 
#include 
using namespace std;
//常用查找算法 - find
//查找 内置数据类型
void test01()
{
	vectorv;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	//查找容器中是否有5这个元素
	vector::iterator it = find(v.begin(), v.end(), 5);
	if (it == v.end())
	{
		cout << "没有找到" << endl;
	}
	else
	{
		cout << "找到了:" << *it << endl;
	}
	it = find(v.begin(), v.end(), 50);
	if (it == v.end())
	{
		cout << "没有找到" << endl;
	}
	else
	{
		cout << "找到了:" << *it << endl;
	}
}
//查找自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		m_age = age;
		m_name = name;
	}
	//重载==号
	bool operator==(const Person& p)
	{
		if (m_age == p.m_age && m_name == p.m_name)
			return true;
		else
			return false;
	}
	string m_name;
	int m_age;
};
void test02()
{
	vectorv;
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	Person pp("bbb", 20);
	vector::iterator it = find(v.begin(), v.end(), pp);
	if (it == v.end())
	{
		cout << "没有找到" << endl;
	}
	else
	{
		cout << "找到了! 姓名:" << it->m_name<<"\t年龄:"<m_age<< endl;
	}
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

总结:find可以在容器中找指定的元素,返回值是迭代器

5.2.2find_if

功能描述:按条件查找元素

函数原型:

  • find_if(iterator beg,iterator end,_Pred);

//按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置。

//beg开始迭代器

//end结束迭代器

//_pred函数或者谓词(返回bool类型的仿函数)

示例:

#include
#include
#include 
using namespace std;
//常用查找算法-find_if
//1.查找内置数据类型
class GreaterFive
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}
};
void test01()
{
	vector v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector::iterator it = find_if(v.begin(), v.end(), GreaterFive());
	if (it == v.end())
	{
		cout << "未找到" << endl;
	}
	else
	{
		cout << "找到了 数字为:" << *it << endl;
	}
}
//2.查找自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}
	string m_name;
	int m_age;
};
class Greater20
{
public:
	bool operator()(Person& p)
	{
		return p.m_age > 20;
	}
};
void test02()
{
	vector v;
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	//年龄大于20
	vector::iterator it = find_if(v.begin(), v.end(), Greater20());
	if (it == v.end())
	{
		cout << "未找到" << endl;
	}
	else
	{
		cout << "找到了 姓名:" << it->m_name<<"\t年龄:"<m_age << endl;
	}
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

5.2.3adjacent_find

功能描述:查找相邻重复元素

函数原型:

  • adjacent_find(iterator beg,iterator end);     //查找相邻重复元素,返回相邻元素的第一个迭代器 ,如果没有,返回end()

示例:

#include 
#include 
#include 
using namespace std;
//常用的查找算法-adjacent_find
void test01()
{
	vector v;
	v.push_back(0);
	v.push_back(2);
	v.push_back(0);
	v.push_back(3);
	v.push_back(1);
	v.push_back(4);
	v.push_back(3);
	v.push_back(3);
	vector::iterator pos = adjacent_find(v.begin(), v.end());
	if (pos == v.end())
	{
		cout << "未找到相邻重复元素" << endl;
	}
	else
	{
		cout << "找到相邻重复元素: " << *pos << endl;
	}
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:面试题中如果出现了查找相邻重复元素的题目,记得用STL中的adjacent_find算法。

功能描述:查找指定元素是否存在

函数原型:

  • bool binary_search(iterator beg,iterator end,value);    //查找指定的元素,查到返回true,否则返回false      //注意在无序序列中不可用

示例:

#include 
#include 
#include 
using namespace std;
//常用查找算法 binary_research

void test01()
{
	vector v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	//查找容器中是否有9元素
	//注意容器必须是有序的数列
	bool result = binary_search(v.begin(), v.end(), 9);
	if (result)
	{
		cout << "找到了元素" <

总结:二分查找法查找效率高,值得注意的是查找的容器元素必须是有序序列。

5.2.5count

功能:统计元素个数

函数原型:

  • count(iterator beg,iterator end,value);  //统计元素出现次数

示例:

#include 
#include 
using namespace std;
//常用统计算法-count
//1.统计内置数据类型
void test01()
{
	vector v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(30);
	v.push_back(40);
	v.push_back(20);
	v.push_back(40);
	int num = count(v.begin(), v.end(), 40);
	cout << "40元素的个数为" << num << endl;
}
//2.统计自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		m_age = age;
		m_name = name;
	}
	bool operator == (const Person& p)
	{
		if (m_age == p.m_age)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	string m_name;
	int m_age;
};
void test02()
{
	vectorv;
	Person p1("刘备" ,35);
	Person p2("关羽" ,35);
	Person p3("张飞" ,35);
	Person p4("赵云" ,30);
	Person p5("曹操" ,40);
	Person p("诸葛亮", 35);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
	int num = count(v.begin(), v.end(), p);
	cout << "与诸葛亮一样岁数的人有多少个:" << num << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

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

5.2.6count_if

功能:按条件同级元素个数

函数原型:

  • count_if(iterator beg,iterator end,_Pred);  //_Pred谓词

示例:

#include 
#include 
#include 
using namespace std;
//常用统计算法-count_if
//1.统计内置数据类型
class Greater20
{
public:
	bool operator()(int val)
	{
		return val > 20;
	}
};
void test01()
{
	vector v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(30);
	v.push_back(40);
	v.push_back(40);
	v.push_back(20);
	int num = count_if(v.begin(), v.end(), Greater20());
	cout << "大于20的元素个数为" << num << endl;
}
//2.统计自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		m_age = age;
		m_name = name;
	}
	string m_name;
	int m_age;
};
class AgeGreater20
{
public:
	bool operator()(const Person &p)
	{
		return p.m_age > 20;
	}
};
void test02()
{
	vectorv;
	Person p1("刘备", 35);
	Person p2("关羽", 35);
	Person p3("张飞", 35);
	Person p4("赵云", 30);
	Person p5("曹操", 40);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
	int num = count_if(v.begin(), v.end(), AgeGreater20());
	cout << "大于20岁的人员人数为:" << num << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

5.3常用排序算法

算法简介:

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

5.3.1sort

功能描述:对容器内元素进行排序

函数原型:

  • sort(iterator beg,iterator end,_Pred);  //_Pred谓词

示例:

#include 
#include 
#include 
#include 
using namespace std;
//常用排序算法-sort
void myPrint(int val)
{
	cout << val << " ";
}

void test01()
{
	vector v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(50);
	v.push_back(20);
	v.push_back(40);
	//利用sort进行升序
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;
	//改变为降序
	sort(v.begin(), v.end(), greater());
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

5.3.2random_shuffle

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

函数原型:

  • random_shuffle(iterator beg,iterator end); //指定范围内的元素随即调整次序

示例:

#include 
#include
#include 
using namespace std;
#include 
//常用排序算法 random_shuffle
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	srand((unsigned int)time(NULL));
	vector v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	//利用洗牌算法打乱顺序
	random_shuffle(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:random_shuffle洗牌算法比较实用,使用时记得加随机种子。

5.3.3merge

功能描述:两个容器元素合并,并存储到另一个容器中。

函数原型:

  • merge(iterator beg1,iterator end1,iterator beg1,iterator end2,iterator dest);//注意:两个容器必须是有序的   dest目标容器开始迭代器

示例:

#include 
#include 
#include 
using namespace std;
//常用排序算法 merge
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v1;
	vectorv2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 1);
	}
	//目标容器
	vector v;
	v.resize(v1.size() + v2.size());
	merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	for_each(v.begin(), v.end(), myPrint);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

5.3.4reverse

功能描述:将容器内元素进行反转

函数原型:

  • reverse(iterator beg,iterator end);  //反转指定范围元素

示例:

#include 
#include
#include 
using namespace std;
//常用排序算法 reverse
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(50);
	v.push_back(40);
	v.push_back(20);
	cout << "反转前:" << endl;
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;
	reverse(v.begin(), v.end());
	cout << "反转后:" << endl;
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 总结: reverse反转指定区间内元素,面试题可能用到

5.4常用拷贝和替换算法

学习目标:掌握常用的拷贝和替换算法。

算法简介:

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

5.4.1copy

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

函数原型:

  • copy(iterator beg,iterator end,iterator dest);   

示例:

#include 
#include
#include 
using namespace std;
//常用拷贝和替换算法-copy
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	vector v2;
	v2.resize(v1.size());
	copy(v1.begin(), v1.end(), v2.begin());
	for_each(v2.begin(), v2.end(), myPrint);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:利用copy算法拷贝时,记得提前给目标容器开辟空间

5.4.2replace

功能描述:将容器内指定范围的就元素改为新元素

函数原型:replace(iterator beg,iterator end,oldvalue,newvalue);

示例:

#include 
#include 
#include 
using namespace std;

//常用拷贝和替换算法 replace
class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
void test01()
{
	vector v;
	v.push_back(20);
	v.push_back(30);
	v.push_back(50);
	v.push_back(30);
	v.push_back(40);
	v.push_back(20);
	v.push_back(10);
	v.push_back(20);
	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
	replace(v.begin(), v.end(), 20, 2000);
	cout << "替换后:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

5.4.3replace_if

功能描述:将区间内满足条件的元素,替换成指定元素。

函数原型:

  • replace_if(iterator beg,iterator end,_Pred,newvalue);  //_Pred谓词

示例:

#include 
using namespace std;
#include 
#include 
//常用拷贝和替换算法 replace_if
class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
class Greater30
{
public:
	bool operator()(int val)
	{
		return val >= 30;
	}
};
void test01()
{
	vector v;
	v.push_back(10);
	v.push_back(20);
	v.push_back(40);
	v.push_back(40);
	v.push_back(30);
	v.push_back(50);
	v.push_back(20);
	v.push_back(10);
	v.push_back(20);

	cout << "替换前:" <

5.4.4swap

功能描述:互换两个容器的元素

函数原型:

  • swap(container c1,container c2); //互换两个容器的元素

示例:

#include 
#include 
#include 
using namespace std;
//常用拷贝和替换算法-swap
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v1;
	vector v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 100);
	}
	cout << "交换前:" << endl;
	for_each(v1.begin(), v1.end(), myPrint);
	cout << endl;
	for_each(v2.begin(), v2.end(), myPrint);
	cout << endl;
	swap(v1, v2);
	cout << "交换后:" << endl;
	for_each(v1.begin(), v1.end(), myPrint);
	cout << endl;
	for_each(v2.begin(), v2.end(), myPrint);
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:swap交换容器时,注意交换的容器要同种类型。

5.5常用算术生成算法

注意:算数生成算法属于小型算法,使用时包含头文件#include

算法简介:

  • accumulate //计算容器元素累计之和
  • fill            //向容器中添加元素

5.5.1accumulate

功能描述:计算区间内 容器累计之和

函数原型:

  • accumulate(iterator beg,iterator end,value);      //value   起始累加值

示例:

#include 
#include
#include 
using namespace std;
//常用的算术生成算法 
void test01()
{
	vector v;
	for (int i = 0; i <= 100; i++)
	{
		v.push_back(i);
	}
	int total = accumulate(v.begin(), v.end(), 0);
	cout << total << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

5.5.2fill

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

函数原型:

  • fill(iterator beg,iterator end,value);        //value填充的值

示例:

#include 
#include 
#include 
#include 
using namespace std;
//常用算术生成算法-fill
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v;
	v.resize(10);
	//后期重新填充
	fill(v.begin(), v.end(), 100);
	for_each(v.begin(), v.end(), myPrint);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

5.6常用集合算法

算法简介:

  • set_intersection   //求两个容器交集
  • set_union          //求两个容器的并集
  • set_difference    //求两个容器的差集
  • C++提高编程(泛型编程与STL技术)_第13张图片

5.6.1set_intersection

功能描述:求两个集合的交集

函数原型:

  • set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);  //iterator dest 目标容器起始迭代器      注意:两原容器中必须是有序序列! 返回的是交集中最后一个元素的位置

 示例:

#include 
#include 
#include 
using namespace std;
//常用集合算法-set_intersection
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v1;
	vector v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 5);
	}
	vector v;
	v.resize(min(v1.size(),v2.size()));
	vector::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	for_each(v.begin(), itEnd, myPrint);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:

  • 求交集的两个集合必须是有序序列
  • 目标容器开辟空间需要从两个容器中取最小值
  • set_intersection返回值是交集中最后一个元素的位置

5.6.2set_union

功能描述:求两个集合的并集

函数原型:

  • set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);  //iterator dest 目标容器起始迭代器      注意:两集合必须是有序序列! 返回的是交集中最后一个元素的位置

示例:

#include 
#include 
#include 
using namespace std;
//常用集合算法-set_union
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v1;
	vector v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 5);
	}
	vector v;
	v.resize(v1.size()+v2.size());
	vector::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	for_each(v.begin(), itEnd, myPrint);
	cout << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:

  • 求并集的两个集合必须是有序序列
  • 目标容器开辟空间需要两个容器相加
  • set_union返回值是并集中最后一个元素的位置

5.6.3set_difference

功能描述:求两个集合的差集

函数原型:

  • set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);  //iterator dest 目标容器起始迭代器      注意:两集合必须是有序序列!

示例:

#include 
#include 
#include 
using namespace std;
//常用集合算法 set_difference
void myPrint(int val)
{
	cout << val << " ";
}
void test01()
{
	vector v1;
	vector v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 5);
	}
	vector v;
	v.resize(max(v1.size(), v2.size()));
	//v1-v2
	vector::iterator itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	cout << "v1-v2:";
	for_each(v.begin(), itEnd, myPrint);
	cout << endl;
	//v2-v1
	itEnd = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v.begin());
	cout << "v2-v1:";
	for_each(v.begin(), itEnd, myPrint);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:

  • 求差集的两个集合必须是有序序列
  • 目标容器开辟空间需要两个容器大小取最大值
  • set_difference返回值是差集中最后一个元素位置

你可能感兴趣的:(c++,c++)