【C++】模板和STL库

  • Post author:
  • Post category:其他


本文是我在学习C++过程当中的心得和学习笔记,在学习C++时已经有C语言的基础,因此入门知识省略了一部分。文章包含了C++的入门基础内容和核心进阶内容,并附上了学习的代码,仅供大家参考。如果有问题,有错误欢迎大家留言。剩余的内容可以通过




这篇文章






找到。



一、 STL标准模板库

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

STL(Standard Template Library,标准模板库)。STL从广义上分为:

容器(container)、算法(algorithm)、迭代器(iterator)

。容器和算法之间通过迭代器进行无缝连接。STL几乎所有的代码都采用了模板类或者模板函数。

在这里插入图片描述

在这里插入图片描述

STL的容器就是将运用最广泛的一些数据结构实现出来,例如数组,链表,树,栈,队列,集合,映射表等等。

算法有拷贝,替换,查找,计数,寻找极值等等。迭代器则非常类似与指针提供一种方法,依序寻访某个容器所含的各个元素。



二、各大容器



2.1 vector容器

c++中的vector容器和数组非常相似,

也称为单端数组,仅支持尾部插入(头部插入需要将所有元素后移,然后插入效率低)

。与普通数组不同之处在于其可以动态扩展。动态扩展不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。

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

在这里插入图片描述



2.1.1 vector的初始化


vector构造函数如下



在这里插入图片描述


vector的初始化

  • 不带参数的构造函数初始化
//初始化一个size为0的vector
vector<int> abc;
  • 带参数的构造函数初始化
//初始化size,但每个元素值为默认值
vector<int> abc(10);    //初始化了10个默认值为0的元素
//初始化size,并且设置初始值
vector<int> cde(101);    //初始化了10个值为1的元素
  • 通过数组地址初始化
int a[5] = {1,2,3,4,5};
//通过数组a的地址初始化,注意地址是从0到5(左闭右开区间)
vector<int> b(a, a+5);
  • 通过同类型的vector初始化
vector<int> a(5,1);
//通过a初始化
vector<int> b(a);
  • 通过insert初始化
//insert初始化方式将同类型的迭代器对应的始末区间(左闭右开区间)内的值插入到vector中
vector<int> a(6,6);
vecot<int> b;
//将a[0]~a[2]插入到b中,b.size()由0变为3
b.insert(b.begin(), a.begin(), a.begin() + 3);

参考文章

vector的几种初始化及赋值方式


遍历程序如下

/*
	容器:vecor
	算法:for_each
	迭代器:vector<int>::iterator
*/
void my_print1(int val)
{
	cout << val << "  ";
}
void my_print(vector <int>&v)
{
	 通过迭代器访问容器中的数据
	vector<int>::iterator itBegin = v.begin(); // 起始迭代器,指向容器中的第一个元素
	vector<int>::iterator itEnd = v.end(); // 结束迭代器,指向容器中的最后一个元素的下一个位置
	 第一种遍历方式
	//while (itBegin != itEnd)
	//{
	//	cout << *itBegin << "  ";
	//	itBegin++;
	//}
	// 第二种遍历方式
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << "  ";
	}
	// 第三种遍历方式
	//for_each(v.begin(), v.end(), my_print1);
	cout << endl;
	// 第四种遍历方式,使用auto
	//for (auto e : v)
	//{
	//	cout << e << " ";
	//}
	//cout << endl;
}
void test1()
{
	// vector构造
	vector<int>v1;	// 创建vector容器,默认构造,无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);	// 向容器中插入数据
	}

	vector<int>v2(v1.begin(), v1.end());	// 通过区间方式进行构造,相当于让v1等于v2
	vector<int>v3(10, 100);	
	vector<int>v4(v3);	// 拷贝构造
	my_print(v1);
	my_print(v2);
	my_print(v3);
	my_print(v4);
	// vector赋值
	v3 = v2;
	v4.assign(v1.begin(), v1.end());
	my_print(v3);
	my_print(v4);
	v4.assign(10, 100);
	my_print(v4);
}



2.1.2 vector通用函数

vector容量、大小、插入、删除和访问元素:

在这里插入图片描述

在这里插入图片描述

void my_print(vector <int>&v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << "  ";
	}
	cout << endl;
}
void test1()
{
	vector<int>v1;	
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	my_print(v1);
	if (v1.empty())
	{
		cout << "v1为空" << endl;
	}
	else
	{
		cout << "v1不为空" << endl;
		cout << "v1的容量为:" << v1.capacity() <<endl;
		cout << "v1的大小为:" << v1.size() << endl;
	}

	cout << "重新指定大小:" << endl;
	v1.resize(15, 100);	// 如果重新指定的长度比原来长了,默认用0填充,这里是用100填充
	my_print(v1);
	v1.resize(5);
	my_print(v1);	// 如果短了,超出部分就删除掉

	cout << "删除:" << endl;
	v1.pop_back();	// 尾删
	v1.erase(v1.begin());	// 传入迭代器参数
	my_print(v1);

	cout << "插入:" << endl;
	v1.insert(v1.begin(), 100);	// 第一个参数是迭代器
	v1.insert(v1.begin(), 2, 50);
	my_print(v1);
	
	cout << "访问元素:" << endl;
	cout << v1[0] << " " << v1.at(1) << endl;
	cout << "第一个元素为:" << v1.front() << endl;
	cout << "最后一个元素为:" << v1.back() << endl;

	cout << "清空:" << endl;
	v1.clear();
	my_print(v1);
}



2.1.3 vector自定义类型+嵌套vector


vector+自定义类型程序如下

class Person
{
public:
	Person(string name, int age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	void showPerson()
	{
		cout << "姓名:" << this->m_name << "年龄:" << this->m_age << endl;
	}
	string m_name;
	int m_age;
};
void test1()
{
	vector <Person>v;
	Person P1("Tom", 20);
	Person P2("Tom", 20);
	Person P3("Tom", 20);
	v.push_back(P1);	// 向容器中插入数据
	v.push_back(P2);
	v.push_back(P3);
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it).m_name << " 年龄:" << (*it).m_age << endl;
		//cout << "姓名:" << it->m_name << " 年龄:" << it->m_age << endl;
	}
}
void test2()
{
	vector <Person*>v;
	Person P1("Tom", 20);
	Person P2("Tom", 20);
	Person P3("Tom", 20);
	v.push_back(&P1);	// 向容器中插入数据
	v.push_back(&P2);
	v.push_back(&P3);
	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) // it是一个二级指针,指向Person*的指针,解引用后是指向P1/P2/P3的指针,指针取数据的操作符为->
	{
		cout << "::姓名:" << (*it)->m_name << " 年龄:" << (*it)->m_age << endl;
	}
}


嵌套vector程序如下

void test1()
{
	vector <vector <int> >v;
	// 创建小容器
	vector <int>v1;
	vector <int>v2;
	vector <int>v3;
	// 向小容器中添加数据
	for (int i = 0; i < 3; i++)
	{
		v1.push_back(i + 1);
		v2.push_back(i + 2);
		v3.push_back(i + 3);
	}
	// 将小容器插入到大容器中
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	// 通过大容器,把所有数据都遍历一遍
	for (vector < vector <int> >::iterator it = v.begin(); it != v.end(); it++)
	{
		// (*it) 是小容器 vector<int>
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
		{
			cout << *vit << " " << endl;
		}
		cout << endl;
	}
}



2.1.4 vector与swap函数


利用swap函数实现收缩内存

void my_print(vector <int>&v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << "  ";
	}
	cout << endl;
}
void test1()
{
	vector<int>v1, v2;	
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(10 - i);
	}
	cout << "交换前:" << endl;
	my_print(v1);
	my_print(v2);
	v1.swap(v2);
	cout << "交换后:" << endl;
	my_print(v1);
	my_print(v2);
}
// 实际应用
// 巧用swap可以收缩内存空间
void test2()
{
	vector<int>v;
	for (int i = 0; i < 10000; 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;
	vector<int>(v).swap(v);	// vector<int>(v)创建一个匿名对象,大小为当前v的大小,然后两个容器指针互换,匿名对象用完后空间就释放了,v的指针指向容量为3的内存空间。
	cout << "v的容量为:" << v.capacity() << endl;
	cout << "v的大小为:" << v.size() << endl;
}


利用swap函数预留空间

void test1()
{
	vector<int>v1;	
	int* p = NULL;
	int num = 0;
	//v1.reserve(10000);	// 多次开辟新空间,较为繁琐,可以利用reserve预留空间,以防数据容量过大,重新开辟为新空间的操作
	for (int i = 0; i < 10000; i++)
	{
		v1.push_back(i);
		if (p != &v1[0])	// 统计一共开辟了多少次空间
		{
			p = &v1[0];
			num++;
		}
	}
	cout << "num = " << num << endl;
}



2.2 string容器

string是C++风格的字符串,而string本质上是一个类。string和char*(char

是一个指针)区别:string是一个类,类内部封装了char

,管理这个字符串,是一个char*型的容器。因此string内部的索引和数组一样是从0开始。

string类内部封装了很多成员方法,例如:查找find,拷贝copy,删除delete替换replace,插入insert。string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

在这里插入图片描述

void test1()
{
	string s1;	// 默认构造
	const char* str = "hello world";
	string s2("hello c++");
	string s3(str);
	string s4(4, 'a');
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s4 = " << s4 << endl;
}

在这里插入图片描述

void test1()
{
	string s1;	
	s1 = "hello world";
	string s2 = s1;
	string s3;
	s3 = 'a';
	string s4;
	s4.assign("hello world");
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2<< endl;
	cout << "s3 = " << s3 << endl;
	cout << "s4 = " << s4 << endl;
}

总结:string赋值的方式很多,一般只是用=方式赋值,简单实用。

string字符串拼接:

在这里插入图片描述

void test1()
{
	string s1 = "我";
	s1 += "爱你";	// 添加一个字符串""
	s1 += ':';	// 添加一个字符''
	string s2 = "中国";
	s1 += s2;
	string s3 = "I";
	s3.append(" love you");
	string s4  ;
	s3.append(": China abcde", 7);	// 将前七个字符加入字符串
	s3.append("1234!6789", 4,1);	// 第五个字符开始,往后一个添加如字符串
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2<< endl;
	cout << "s3 = " << s3 << endl;
	cout << "s4 = " << s4 << endl;
}

string字符串查找和替换:
在这里插入图片描述

void test1()
{
	string s1 = "abcdef";	// 索引从零开始
	int pos = s1.find("de");
	if (pos == -1)
	{
		cout << "未找到字符串" << endl;
	}
	else
	{
		cout << "找到字符串pos = " << pos << endl;
	}
	pos = s1.rfind("de");	// rfind是从右往左差,find从左往右找
	cout << "找到字符串pos = " << pos << endl;
	// 从索引1起,三个字符,替换成1111,即bcd换成1111
	s1.replace(1, 3, "1111");
	cout << "s1 =  " << s1 << endl;
}

string字符串比较实质上是依次按字符的ASCII进行对比,若相等返回0,大于返回1,小于返回-1。若第一个字符相等,则比较第二个,直至比较出大小或者字符串结束。

void test1()
{
	string s1 = "dbc";	
	string s2 = "abc";

	if ( s1.compare(s2) == 0)
	{
		cout << "相等" << endl;	// 一般是比较是否相等
	}
	else if(s1.compare(s2) == 1)
	{
		cout << "大于" << endl;
	}
	else
		cout << "小于" << endl;
}

string字符串存取:

void test1()
{
	string s1 = "hello world";	
	// 1、通过[]访问单个字符
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
	// 2、通过at方式访问单个字符
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1.at(i) << " ";
	}
	cout << endl;
	// 修改单个字符
	s1[0] = 'x';
	s1.at(1) = 't';
	cout << s1 << endl;
}

string字符串插入和删除:

在这里插入图片描述

void test1()
{
	string s1 = "hello world";	
	s1.insert(1, "111");	// 插入,从索引1的位置开始插入
	cout << s1 << endl;
	s1.erase(1, 3);	// 删除,从索引1的位置开始删除
	cout << s1 << endl;
}

string字符串的子串获取:

// 从字符串中获取子串
void test1()
{
	string s1 = "hello world";	
	string subStr1 = s1.substr(1, 3);	// 从索引1位置开始,往后3个字符
	cout << "subStr1 = " << subStr1 << endl;
	// 实用操作 ---- 从邮件地址中获取用户名信息
	string email = "zhangsan@sina.com";
	int pos = email.find("@");
	string username = email.substr(0, pos);
	cout << "username = " << username << endl;
}



2.3 deque容器

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

在这里插入图片描述

在这里插入图片描述

deque容器和vector容器很相似,这里只给出函数原型。

构造函数原型:

在这里插入图片描述

赋值函数原型:

在这里插入图片描述

deque没有容量的概念,除了没有容量以外其他全部和vector一样:

在这里插入图片描述

插入、删除:

在这里插入图片描述

元素的存取:

在这里插入图片描述

deque排序:

void my_print(const deque <int>& d)
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << "  ";
	}
	cout << endl;
}
void test1()
{
	deque<int>d1;	
	d1.push_back(2);
	d1.push_back(55);
	d1.push_back(18);
	d1.push_back(99);
	my_print(d1);
	sort(d1.begin(), d1.end());	// 默认升序,对于支持随机访问的迭代器的容器(vector deque),都可以利用sort算法直接进行排序
	cout << "排序后:" << endl;
	my_print(d1);
}



2.4 stack容器

stack是一种先进后出的容器。栈可以为空,叫空栈。可以返回元素个数。栈中进入数据叫入栈,出数据叫出栈。

在这里插入图片描述

在这里插入图片描述

void test1()
{
	stack<int>s;
	s.push(10);
	s.push(20);
	s.push(30);
	s.push(40);
	cout << "栈的大小为:" << s.size() << endl;
	while (!s.empty())
	{
		cout << "栈顶元素为:" << s.top() << endl;
		s.pop();
	} 
	cout << "栈的大小为:" << s.size() << endl;
}



2.5 queue容器

queue是一种先进先出的容器。队列允许从一端新增元素,另一端移除元素。只有对头和队尾才能被外界使用,因此队列不允许有遍历行为。队列中进数据叫入队,出数据叫出队。

在这里插入图片描述

在这里插入图片描述



2.6 list容器

数据以链式存储,list是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链实现的。链表由一系列节点组成, 节点包括存储数据的数据域和存放下一个数据地址的指针域。

链表的优点:采用动态内存分配,不会造成内存浪费和溢出;可以对任意位置进行插入或者删除元素,修改指针即可,不需要移动大量元素。缺点:容器的遍历速度没有数组快,占用空间比数据大。

在这里插入图片描述

链表当中的迭代器只支持前移和后移,属于双向迭代器。在STL容器当中,list和vector容器是最常用的两个容器。

list构造函数:

在这里插入图片描述

list赋值函数:

在这里插入图片描述

list大小操作函数:

在这里插入图片描述

list插入和删除函数:

在这里插入图片描述

list数据存取函数,不支持at和[ ]操作。原因是list本质链表,不是用连续线性空间存储数据的,迭代器也是不支持随机访问的。

在这里插入图片描述

void test1()
{
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	// L1[0];	// 报错,不支持用[]访问list容器中的元素
	// L1.at(0);	// 报错,不支持用at访问list容器中的元素
	cout << "第一个元素为:" << L1.front() << endl;
	cout << "最后一个元素为:" << L1.back() << endl;
	list<int>::iterator it = L1.begin();
	it++;
	it--;
	//it = it + 1;	// 报错,如果成立那么也就可以+2,跳着访问(随机访问),而链表不允许随机访问。
	//原因是list本质链表,不是用连续线性空间存储数据的,迭代器也是不支持随机访问的。
}

反转和排序:

在这里插入图片描述

所有不支持随机访问迭代器的容器,不可以用标准算法,例如sort( ),不支持随机访问迭代器的容器,内部会提供对应的一些算法。这里只能用L.sort();,不能用sort(L.begin(), L.end());。



2.7 set容器

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

在这里插入图片描述

set容器的大小和交换:

在这里插入图片描述

set容器的插入和删除:

在这里插入图片描述

set容器的查找和计算元素个数:
在这里插入图片描述

set容器和multiset的区别:

在这里插入图片描述

set容器排序规则。默认排序规则为从小到大,如何改变排序规则?

class Mycompare
{
public:
	bool operator()(int v1,  int v2) const
	{
		return v1 > v2;
	}
};
void my_print1(const set <int>& s)
{
	for (set<int>::const_iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << "  ";
	}
	cout << endl;
}
void my_print2(const set <int, Mycompare>& s)
{
	for (set<int, Mycompare>::const_iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << "  ";
	}
	cout << endl;
}
void test1()
{
	set<int>s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(50);
	s1.insert(30);
	my_print1(s1);
	set<int, Mycompare>s2;
	s2.insert(10);
	s2.insert(40);
	s2.insert(20);
	s2.insert(50);
	s2.insert(30);
	my_print2(s2);
}

set容器自定义类型的自定义排序:

class Person
{
public:
	Person(string name, int age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	string m_name;
	int m_age;
};
class Mycompare
{
public:
	bool operator()(const Person &p1, const Person &p2) const
	{
		return p1.m_age > p2.m_age;
	}
};
void test1()
{
	set<Person, Mycompare>s;
	Person p1("张三", 18);
	Person p2("李四", 98);
	Person p3("王五", 8);
	Person p4("赵六", 28);
	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);
	for (set<Person, Mycompare>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << "姓名:" << it->m_name << " 年龄:" << it->m_age << endl;
	}
}



2.8 pair对组

成对出现的数据,利用对组可以返回两个数据。不需要包含头文件。

在这里插入图片描述

void test1()
{
	pair<string, int>p("Tom", 20);
	cout << "姓名:" << p.first << " 年龄: " << p.second << endl;
	pair<string, int>p2 = make_pair("Jerry", 18);
	cout << "姓名:" << p2.first << " 年龄: " << p2.second << endl;
}



2.9 map容器

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

map/multimap属于关联式容器,底层结构是用二叉树实现。优点:可以根据key值快速找到value值。

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

在这里插入图片描述

void my_print(map<int, int>& m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << (*it).first << " value = " << it->second << endl;
	}
}
void test1()
{
	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));
	my_print(m);
}

map容器的大小和交换:

在这里插入图片描述

map容器的插入、删除、排序:

在这里插入图片描述

class mycompare
{
public:
	bool operator()(int v1, int v2) const
	{
		return v1 > v2;
	}
};
void my_print(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;
	}
}
void test1()
{
	map<int, int, mycompare>m;
	m.insert(pair<int, int>(1, 10)); // 第一种插入
	m.insert(make_pair(2, 20));		// 第二种插入
	m.insert(map<int, int>::value_type(3, 30)); // 第三种插入
	m[4] = 40; // 第四种插入
	my_print(m); 
}

map容器的查找和计数:

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



三、 STL函数对象

在这里插入图片描述

在这里插入图片描述

class Myadd
{
public:
	int operator()(int v1, int v2) const
	{
		return v1 + v2;
	}
};
// 1、函数对象在使用时,可以向普通函数那样调用,可以有参数,可以有返回值
void test1()
{
	Myadd myadd;
	cout << myadd(10, 10) << endl;
}
// 2、函数对象超出普通函数的概念,函数对象可以有自己的状态
class Myprint
{
public: 
	Myprint()
	{
		this->counter = 0;
	}
	void operator()(string test)
	{
		cout << test << endl;
		this->counter++;
	}
	int counter;
};
void test2()
{
	Myprint myprint;
	myprint("hello world");
	myprint("hello world");
	myprint("hello world");
	cout << "myprint调用次数为: " << myprint.counter << endl;
}
// 3、函数对象可以作为参数传递
void doprint(Myprint &mp, string test)
{
	mp(test);
}
void test3()
{
	Myprint myprint;
	doprint(myprint, "hello c++");
}



3.1 谓词

在这里插入图片描述

class GreaterFive
{
public:
	bool operator()(int val)	// 一元谓词
	{
		return val > 5;
	}
};
void test1()
{
	vector<int>v;
	for (int i = 1; i < 6; i++)
	{
		v.push_back(i*10);
	}
	vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
	if (it == v.end())
	{
		cout << "未找到" << endl;
	}
	else
	{
		cout << "找到了大于5的数字为:" << *it << endl;
	}
}
class mycompare
{
public:
	bool operator()(int val1, int val2) const	// 二元谓词
	{
		return val1 > val2;
	}
};
void myprint(vector <int>& v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end();it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
}
void test1()
{
	vector<int>v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(20);
	v.push_back(30);
	v.push_back(50);
	myprint(v);
	sort(v.begin(), v.end());	// 默认是升序
	myprint(v);
	sort(v.begin(), v.end(), mycompare());
	//sort(v.begin(), v.end(), greater<int>());	// 两句作用相同
	myprint(v);
}



3.2 内建函数对象

STL内建了一些函数对象,可以分为算术仿函数,关系仿函数,逻辑仿函数,这些仿函数所产生的对象,用法和一般函数完全相同,使用内建函数对象,需要引用头文件functional。

在这里插入图片描述

在这里插入图片描述

void test1()
{
	negate<int>n;
	cout << n(50) << endl;
	plus<int>p;	// 二元运算默认是同种型相加
	cout << p(10, 20) << endl;
}

在这里插入图片描述

void myprint(vector <bool>& v)
{
	for (vector<bool>::iterator it = v.begin(); it != v.end();it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
}
void test1()
{
	vector<bool>v;
	v.push_back(true);
	v.push_back(false);
	v.push_back(true);
	v.push_back(false);
	myprint(v);
	// 利用逻辑非 将容器v搬运到容器v2中 并执行取反操作
	vector<bool>v2;
	v2.resize(v.size());
	transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
	myprint(v2);
}



四、 STL常用算法

在这里插入图片描述



4.1 遍历算法

在这里插入图片描述

在这里插入图片描述

for_each是实际开发当中最常用的遍历算法,需要熟练掌握。transform具体用法可见:3.4.10.2 内建函数对象,_func是搬运的规则。

// 普通函数
void normal_print(int val)
{
	cout << val << " ";
}
// 仿函数
class print2
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
void myprint(vector <bool>& v)
{
	for (vector<bool>::iterator it = v.begin(); it != v.end();it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
}
void test1()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	for_each(v.begin(), v.end(), normal_print);	// 普通函数只要把函数名放进去
	cout << endl;
	for_each(v.begin(), v.end(), print2());	// 仿函数要把函数对象放进去
	cout << endl;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

void test1()
{
	vector<int>v;
	v.push_back(1);
	v.push_back(5);
	v.push_back(3);
	v.push_back(6);
	v.push_back(9);
	v.push_back(4);
	v.push_back(4);
	v.push_back(2);
	// 查找相邻重复元素
	vector<int>::iterator it = adjacent_find(v.begin(), v.end());
	if (it == v.end())
	{
		cout << "找不到重复元素" << endl;
	}
	else
	{
		cout << "找到相邻重复元素为:" << *it << endl;
	}
}

在这里插入图片描述

在这里插入图片描述

class Person
{
public:
	Person(string name, int age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	bool operator==(const Person& p)
	{
		if (this->m_age == p.m_age)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	string m_name;
	int m_age;
};
void test1()
{
	vector<int>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;
}
void test2()
{
	vector<Person>v;
	Person p1("张三", 35);
	Person p2("李四", 35);
	Person p3("王五", 35);
	Person p4("赵六", 40);
	Person p5("李二", 30);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	Person p6("柳大", 35);
	int num = count(v.begin(), v.end(), p6);
	cout << "和张三同岁数的人数:" << num << endl;
}

在这里插入图片描述



4.2 排序算法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

void myprint(vector <int>& v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end();it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
}
void test1()
{
	srand((unsigned int)time(NULL));	// 需要用ctime头文件
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	random_shuffle(v.begin(), v.end());
	myprint(v);
}

在这里插入图片描述

void myprint(vector <int>& v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end();it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
}
void test1()
{
	srand((unsigned int)time(NULL));	// 需要用ctime头文件
	vector<int>v1;
	vector<int>v2;

	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i+1);
	}
	vector<int>vTarget;
	vTarget.resize(v1.size() + v2.size());
	merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
	myprint(vTarget);
	// 注意两个容器必须是有序的,并且同时都是升序或者都是降序
}

在这里插入图片描述



4.3 拷贝和替换算法

在这里插入图片描述

在这里插入图片描述

使用copy算法在拷贝时,目标木器记得提前开辟空间。

在这里插入图片描述

替换的是将所有旧元素换成新元素。

在这里插入图片描述

在这里插入图片描述

交换的容器要同种类型。



4.4 算术算生成算法

算术生成算法属于小型算法,使用时包含的头文件为numeric。

在这里插入图片描述

在这里插入图片描述

void test1()
{
	vector<int>v;
	for (int i = 0; i <= 100; i++)
	{
		v.push_back(i);
	}
	int total = accumulate(v.begin(), v.end(), 0);	// 参数三是起始累加值
	cout << "total = " << total << endl;
}

在这里插入图片描述

利用fill可以将容器区间内的元素填充为指定的值。



4.5 集合算法

在这里插入图片描述

在这里插入图片描述

void myprint(vector <int>& v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end();it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
}
void print1(const int val)
{
	cout << val << " ";
}
void test1()
{
	vector<int>v1;
	vector<int>v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);	// 0-9
		v2.push_back(i + 5);	// 5-14
	}
	myprint(v1);
	myprint(v2);
	vector<int>vTarget;
	vTarget.resize(min(v1.size(), v2.size()));
	vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, print1);
	cout << endl;
}

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

在这里插入图片描述

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

在这里插入图片描述

总结:求差集的两个集合必须为有序序列,目标容器开辟空间需要从两个容器中取较大值,set_difference返回值既是差集中最后一个元素的位置。



版权声明:本文为qq_45765437原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。