【让C++与ROS共舞】第五篇·与C++的相杀

目录


1. STL初识

为了建立数据结构与算法的一套标准,诞生了STL
所谓STL就是标准模板库,它广义上分为:容器、算法、迭代器。容器和算法之间通过迭代器进行无缝衔接,STL所有代码几乎都采用了模板类或模板函数。
STL有六大组件:容器、算法、迭代器、仿函数、适配器、空间配置器。

  1. 容器:各种数据结构
  2. 算法:各种常用算法
  3. 迭代器:扮演了容器与算法之间的粘合剂
  4. 仿函数:行为类似函数,可以作为算法的某种策略
  5. 适配器:一种用来修饰容器或仿函数或迭代器接口的东西
  6. 空间适配器:负责空间的适配与管理

例:vector存放内置数据类型

容器:vector,可以理解为数组
算法:for_each
迭代器:vector< int >::iteractor

语法:vector<数据类型> 容器名

示例如下:
首先是头文件需要额外包含vector(容器)和algorithm(算法)

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>

using namespace std;

创建容器及迭代器

	//创建了一个vector容器
	vector<int> v;

	//向容器中插入数据 尾插法
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

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

遍历vector容器

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

	//第二种遍历方式
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << endl;
	}

	//第三种遍历方式
	for_each (v.begin(), v.end(),printf);

结果如下:
在这里插入图片描述


2. STL常用容器

2.1 string容器

2.1.1 概述

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

string这个类内部封装了C语言的字符串类型char*,并且在这个类的内部还封装了很多成员函数,string管理char*所分配的内存,不用担心赋值越界和取值越界等,由类内部进行负责。

在之前我们写的代码中,用到字符串的数据类型时,往往需要有以下几步操作:

#include<string>//包含头文件

解释:string是一个容器,使用容器时需要调用响应头文件

string s;//创建一个字符串

解释:string是一个类,我们在通过类创建对象时就是这么创建的


2.1.2 string操作

1. 赋值操作

赋值操作有两种实现方法:用“=”赋值、使用assign成员函数进行赋值操作。
用“=”赋值的底层原理其实就是一个运算符重载:string &operator = (const char* s)
通过成员函数赋值的底层原理是:string &assign(const char* s)
一般来说,使用等号进行赋值就很方便了,我们也不用记这个成员函数,这里只是阐明一下底层逻辑。


2. 拼接操作

拼接操作也有两种实现形式:用“+=”实现拼接、使用append成员函数实现拼接。
底层原理和复制操作一样,append这个成员函数可以理解为将括号内的字符串追加到之前字符串的末尾。
示例如下:

string s1 = "我";
string s2 = "爱你";
s1 += s2;
cout << s1 << endl;

结果如下:
在这里插入图片描述
加等号非常的简单常用,但是append有时显得更灵活,它可以截取一段字符串的任意一段拼接到字符串的尾部


3. 查找和替换

在这里插入图片描述
查找和替换的重载版本很多,就不一一讲解了,只挑几个比较常用的进行讲解和演示
示例如下:

//查找
void test01()
{
	string str1 = "abcdef";
	int pos = str1.find("de");

	if (pos == -1)
	{
		cout << "未找到字符串" << endl;
	}
	else
		cout << "找到字符串,pos= " << pos << endl;
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述
rfind和find都是查找的函数,唯一区别就是rfind是从右往左查找,find是从左往右查找,但查到的字符段的下标是相同的。
示例如下:

//替换
void test01()
{
	string str1 = "abcdef";
	str1.replace(1, 3, "1111");//从第1个位置起,到第3个位置的字符串替换为“1111”

	cout << str1 << endl;
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述


4. 比较操作

使用成员函数compare,根据字符的ASCII码排序进行比较,两字符串相等返回0,大于返回1,小于返回-1
示例如下:

void test01()
{
	string str1 = "abcdef";
	string str2 = "abcefas";
	int x = str1.compare(str2);

	if (x == 0)
		cout << "str1 = str2" << endl;
	else if (x == 1)
		cout << "str1>str2" << endl;
	else
		cout << "str1<str2" << endl;
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述
一般来说比较操作都是用来比较两个字符串是否相等的,很少用来比较谁大谁小,因此我们也不用去刻意记ASCII码,将几个常用的记住即可。


5. 插入和删除

插入:使用string成员函数insert(pos,str1)
删除:使用string成员函数erase(pos,int n = npos)从第pos个位置开始删除n个字符
示例如下:

void test01()
{
	string str1 = "abcdef";

	//插入
	str1.insert(1, "xxx");
	cout << str1 << endl;

	string str2 = "222";
	str1.insert(0, str2);
	cout << str1 << endl;

	//删除
	str1.erase(0, 3);
	str1.erase(1, 3);
	cout << str1 << endl;
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述
注意一点就行,插入和删除的起始下标都是从0开始的。


6. 截取子串

截取子串是通过成员函数substr(pos,npos)实现的,它可以截取从pos开始的n个字符的子串
示例如下:

void test01()
{
	string str1 = "abcdef";

	string str2 = str1.substr(1, 3);//此时输出应为bcd
	cout << str2 << endl;
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述


2.2 vector容器

2.2.1 概述

vector容器也叫单端容器,也就是只能在尾部插入和删除元素,不能在前端进行操作。vector容器可以动态扩展,它不会在后面接着扩展内存,而是找一块儿更大的内存空间,将数据拷贝到新的空间。
而且vector 的迭代器是最强大的一款迭代器,它支持随机访问。
创建vector容器的方法在前面有所叙述这里就不赘述了。
在这里插入图片描述


2.2.2 vector操作

1. 赋值操作

vector容器赋值操作也有两种赋值方法:重载等号运算符,直接将一个容器赋值给另一个容器;也可以使用assign成员函数,传入v.begin()和v.end()进行赋值操作。
示例如下:

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

void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10 ; i++)
	{
		v1.push_back(i);
	}

	printf(v1);

	//赋值
	vector<int>v2;
	v2 = v1;
	printf(v2);

	vector<int>v3;
	v3.assign(v1.begin(), v1.end());
	printf(v3);
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述


2. 容量和大小

在这里插入图片描述
示例如下:

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

void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10 ; i++)
	{
		v1.push_back(i);
	}
	printf(v1);

	if (!empty(v1))
	{
		cout << "容器容量为:" << v1.capacity() << endl;
		cout << "容器元素个数为:" << v1.size() << endl;
		v1.resize(20, 2);
	}

	printf(v1);
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述


3. 插入和删除

实现插入和删除可以通过以下几种方式:
在这里插入图片描述
示例如下:

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

void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10 ; i++)
	{
		v1.push_back(i);
	}
	printf(v1);

	//尾插
	v1.push_back(90);
	printf(v1);

	//尾删
	v1.pop_back();
	printf(v1);

	//插入
	v1.insert(v1.begin(), 100);//第一个参数为迭代器
	printf(v1);

	//删除
	v1.erase(v1.begin());
	printf(v1);

	//清除
	v1.clear();
	printf(v1);
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述


4. 数据存取

数据存取可使用以下几种方式:
在这里插入图片描述
示例如下:

void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10 ; i++)
	{
		v1.push_back(i);
	}
	printf(v1);

	//数据存取
	cout << "容器中第3个元素: " << v1.at(3) << " 也可以这样访问:" << v1[3] << endl;
	cout << "容器中第一个元素为:" << v1.front() << endl;
	cout << "容器中最后一个元素为:" << v1.back() << endl;
}

结果如下:
在这里插入图片描述


5. 预留空间

预留空间使用成员函数reverse(int len),它的意思是为容器预留出len个元素长度,但不对其中的元素进行初始化,因此也不可访问预留出的内存空间。
示例如下:

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

void test01()
{
	vector<int>v1;

	int num = 0;//统计开辟次数
	int* p = NULL;

	//未预留空间
	for (int i = 0; i < 10000 ; i++)
	{
		v1.push_back(i);

		if (p != &v1[0])
		{
			p = &v1[0];
			num++;
		}
	}

	cout << "未预留空间时 num= " << num << endl;
	//预留空间后
	vector<int>v2;
	int num2 = 0;

	v2.reserve(10000);
	for (int i = 0; i < 10000; i++)
	{
		v2.push_back(i);

		if (p != &v2[0])
		{
			p = &v2[0];
			num2++;
		}
	}
	cout << "预留空间后 num= " << num2 << endl;
}

结果如下:
在这里插入图片描述


2.3 deque容器

2.3.1 概述

deque容器又叫作“双端数组”,它可以对头端进行插入和删除操作。
deque较vector来说,对头部的删除和插入速度更快,效率更高,但vector访问元素时的速度比deque容器速度快,这与两者的内部实现有关。
在这里插入图片描述
deque容器内部存在一个中控器,维护每段缓冲区的内容,而缓冲区中存放真实的数据,中控器维护的是缓冲区的地址,这样一个机制使得使用deque像一段连续的内存空间。
在这里插入图片描述


2.3.2 deque操作

1. 赋值操作

deque容器的赋值操作与vector容器的复制操作类似,可以用重载等号运算符的方法赋值,也可以使用assign成员函数进行赋值。
在这里插入图片描述
示例如下:

void printf(deque<int> &d)
{
	for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int>d;
	for (int i = 0; i < 10; i++)
	{
		d.push_back(i);
	}
	printf(d);

	deque<int>d2;
	d2 = d;
	printf(d2);

	d2.assign(10, 5);
	printf(d2);
}

int main() {

	test01();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述


2. 大小操作

在这里插入图片描述
大小操作与vector容器类似,但是没有测算容量的操作。
示例如下:

void test01()
{
	deque<int>d;
	for (int i = 0; i < 10; i++)
	{
		d.push_back(i);
	}
	printf(d);

	if (!d.empty()) {
		cout << "deque的长度为:" << d.size() << endl;
		d.resize(15);
		printf(d);
		cout << "deque的长度为:" << d.size() << endl;
	}
	
}

结果如下:
在这里插入图片描述


3. 插入和删除

在这里插入图片描述
示例如下:

void printf(deque<int> &d)
{
	for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int>d;
	for (int i = 0; i < 10; i++)
	{
		d.push_back(i);
	}
	printf(d);

	d.push_back(100);
	d.push_front(100);
	printf(d);

	d.pop_back();
	printf(d);

	d.insert(d.begin(), 100);
	d.insert(d.begin(), 10, 13);
	printf(d);

	deque<int>::iterator it = d.begin();
	it++;
	d.erase(it);//如果要删第二个元素,可以使用这种方法
	printf(d);

	d.clear();
	printf(d);
}

结果如下:
在这里插入图片描述


4. 数据存取操作

deque容器的存取操作与vector容器的存取操作实现形式完全相同,它还提供了两个访问接口,front和back
在这里插入图片描述
示例如下:

void printf(deque<int> &d)
{
	for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int>d;
	for (int i = 0; i < 10; i++)
	{
		d.push_back(i);
	}
	printf(d);

	cout << d[3] << endl;

	cout << d.at(4) << endl;

	cout << "返回第一个元素:" << d.front() << endl;
}

结果如下:
在这里插入图片描述


2.4 stack容器

2.4.1 概述

stack容器是非常重要的一种数据结构,它的特点是先进后出,只有栈顶元素top才可以被外界访问,因此它不能被遍历。
在这里插入图片描述


2.4.2 stack操作

栈和队列部分我数据结构学过C语言详细的实现,因此这里就一略带过了。
在这里插入图片描述
示例如下:

void test01()
{
	stack<int> s;

	s.push(10);
	s.push(20);
	s.push(30);

	while (!s.empty())
	{
		cout << s.top() << endl;
		s.pop();
	}
	cout << s.size() << endl;

}

结果如下:
在这里插入图片描述


2.5 queue容器

2.5.1 概述

queue容器也叫队列,也是一种非常重要的数据结构,它有两个出口,队头和队尾,只能在队头出队,在队尾入队。
在这里插入图片描述


2.5.2 queue操作

在这里插入图片描述
示例如下:

void test01()
{
	queue<int> q;
	q.push(10);
	q.push(20);
	q.push(30);
	q.push(40);

	cout << "队头元素为:" << q.front() << endl;
	cout << "队尾元素为:" << q.back() << endl;

	while (!q.empty())
	{
		cout << "即将出队元素为:" << q.front() << endl;
		q.pop();
	}
	cout << "当前长度为:" << q.size() << endl;

}

结果如下:
在这里插入图片描述


2.6 list容器

2.6.1 概述

list容器又叫链表,它是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过指针链接实现的,链表由结点构成,结点又由两部分组成:数据域和指针域,数据域存放这个结点的数据,指针域指向下一个结点。

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

优点:更方便的对任意位置进行插入和删除操作
缺点:遍历链表速度较慢

链表的种类有很多,比如:带头结点的单链表、带尾结点的单链表、双向列表、循环列表。而STL中的链表是双向循环链表。
在这里插入图片描述


2.6.2 list操作

1. 赋值和交换

链表的赋值和交换操作与之前vector容器的赋值和交换操作类似,都是可以使用assign和swap两种方式进行复制操作,使用swap成员函数进行元素交换。
在这里插入图片描述
示例如下:

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

void test01()
{
	list<int> l;

	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_back(40);
	l.push_back(50);

	printf(l);

	list<int> l2;
	l2 = l;
	printf(l2);

	list<int> l3;
	l3.assign(l.begin(),l.end());
	printf(l3);

	list<int> l4;
	l4.assign(10, 10);
	l4.swap(l);
	printf(l);

}

结果如下:
在这里插入图片描述


2. 大小操作

在这里插入图片描述
示例如下:

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

void test01()
{
	list<int> l;

	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_back(40);
	l.push_back(50);

	printf(l);

	if (!l.empty())
	{
		cout << "容器大小为:" << l.size() << endl;
		l.resize(10);
	}
	printf(l);

}

结果如下:
在这里插入图片描述


3. 插入与删除

在这里插入图片描述
示例如下:

void test01()
{
	list<int> l;

	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_front(100);
	l.push_front(200);

	printf(l);

	l.pop_back();
	printf(l);

	list<int>::iterator it = l.begin();
	it++;
	l.insert(it, 3, 51);
	printf(l);

	it++;
	l.erase(it);
	printf(l);

	l.remove(51);
	printf(l);

	l.clear();
	if (!l.empty())
	{
		cout << "当前容器为空" << endl;
	}

}

结果如下:
在这里插入图片描述


4. 数据存取

在这里插入图片描述
list容器不支持随机访问,而且迭代器也只支持前移和后移
示例如下:

void test01()
{
	list<int> l;

	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_front(100);
	l.push_front(200);

	printf(l);

	cout << "第一个元素:" << l.front() << endl;
	cout << "最后一个元素:" << l.back() << endl;
	list<int>::iterator it = l.begin();
	it++;
	it--;
	it = it + 1;//这样的赋值操作是错误的,支持随机访问的迭代器才可以这样使用
}

2.7 set/multiset容器

2.7.1 概述

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

至于它的底层到底是什么我也没有深入地去剖析,我猜应该是二叉排序数实现的吧。它使用这样一个底层去实现,使用所有元素在插入容器时会被自动排序
set容器和multiset容器也有一定区别:set容器不允许容器中有重复元素,而multiset容器中允许有重复的元素,当你包含了set头文件后,也可以创建multiset容器,而它们的操作基本是一样的,因此我就以set容器为例进行讲解了。


2.7.2 set操作

1. 构造和赋值

在这里插入图片描述
示例如下:

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

void test01()
{
	//默认构造
	set<int> s;
	s.insert(30);
	s.insert(10);
	s.insert(50);
	s.insert(20);

	printf(s);
	
	//拷贝构造
	set<int>s2(s);
	printf(s2);

	set<int>s3 = s;
	printf(s3);
}

结果如下:
在这里插入图片描述


2. 大小和交换

在这里插入图片描述
示例如下:

void test01()
{
	//默认构造
	set<int> s;
	s.insert(30);
	s.insert(10);
	s.insert(50);
	s.insert(20);

	printf(s);
	
	if (!s.empty())
	{
		cout << "容器的大小为:" << s.size() << endl;
	}

	set<int> s2;
	s2.insert(700);
	s2.insert(100);
	s2.insert(200);

	s.swap(s2);
	printf(s);
}

结果如下:
在这里插入图片描述


3. 插入和删除

在这里插入图片描述
示例如下:

void test01()
{
	//默认构造
	set<int> s;
	s.insert(30);
	s.insert(10);
	s.insert(50);
	s.insert(20);

	printf(s);
	
	set<int>::iterator it = s.begin();
	it++;
	s.erase(it);
	printf(s);

	s.erase(50);
	printf(s);

	s.clear();
	printf(s);
	
}

结果如下:
在这里插入图片描述


4. 查找和统计

在这里插入图片描述
示例如下:

void test01()
{
	//默认构造
	set<int> s;
	s.insert(30);
	s.insert(10);
	s.insert(50);
	s.insert(20);

	printf(s);
	
	set<int>::iterator it = s.find(30);

	if (it != s.end())
	{
		cout << "找到元素:" << *it << endl;
	}
	else
		cout << "未找到该数据" << endl;

	cout << "该容器中50的个数为:" << s.count(50) << endl;
	
}

结果如下:
在这里插入图片描述


5. pair对组创建

在这里插入图片描述
示例如下:

void test01()
{
	pair<string, int> p ("K.Fire", 20);

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

	pair<string, int> p2 = make_pair("张连山", 80);

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

结果如下:
在这里插入图片描述


2.8 map/multimap容器

2.8.1 概述

map/multimap容器类似于python中的字典,map中的所有元素都是pair,pair的第一个元素为key(键),起索引作用,第二个元素为value(值),起存储数据作用,pair相当于python中的键值对,当用户向map容器中插入数据时,所有元素会根据键值自动排序。

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

map容器在C++中应用很广,仅次于vector容器和deque容器,在python中的字典也是一种非常好用的数据类型,因为用户可以根据key值快速找到value值。
map与multimap的区别是:map容器不允许容器中有重复的值,而multimap容器允许容器中存在重复的值,其他的操作均类似,因此我也是以map容器为例进行讲解。


2.8.2 map/multimap操作

1. 构造和赋值

在这里插入图片描述
示例如下:

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

void test01()
{
	//默认构造
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(4, 40));

	printf(m);

	map<int, int>m2(m);
	printf(m2);

	map<int, int>m3;
	m3 = m;
	printf(m3);
}

结果如下:
在这里插入图片描述


2. 大小和交换

在这里插入图片描述
示例如下:

void test01()
{
	//默认构造
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(4, 40));

	printf(m);

	if (!m.empty())
	{
		cout << "容器的大小为:" << m.size() << endl;
	}
	else
		cout << "容器为空" << endl;

	map<int, int>m2;
	m2.insert(pair<int, int>(1, 50));
	m2.insert(pair<int, int>(2, 52));
	m2.insert(pair<int, int>(3, 51));
	m2.insert(pair<int, int>(4, 54));

	m.swap(m2);
	printf(m);

}

结果如下:
在这里插入图片描述


3. 插入和删除

在这里插入图片描述
示例如下:

void test01()
{
	//默认构造
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(4, 40));
	m.insert(make_pair(5, 50));
	m[6] = 60;//快捷的方式,不建议这样插入数据,但可以快速访问

	printf(m);

	map<int, int>::iterator it = m.begin();
	it++;
	m.erase(it);
	printf(m);

	m.erase(4);
	printf(m);

	m.clear();
	printf(m);

}

结果如下:
在这里插入图片描述


4. 查找和统计

在这里插入图片描述
示例如下:

void test01()
{
	//默认构造
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(4, 40));
	m.insert(make_pair(5, 50));
	m[6] = 60;//快捷的方式,不建议这样插入数据,但可以快速访问

	printf(m);

	map<int, int>::iterator it;
	it = m.find(4);
	if (it != m.end())
	{
		cout << "key= " << (*it).first << " value= " << (*it).second << endl;
		cout << (*it).first << "的元素个数为:" << m.count(4) << endl;
	}
	else
		cout << "没有该元素" << endl;

}

结果如下:
在这里插入图片描述


3. 函数对象

3.1 函数对象

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

本质:函数对象是一个类,而不是一个函数

特点:

  • 函数对象调用时,可以像普通函数那样调用。可以有参数和返回值
  • 函数对象超出普通函数的概念,可以有自己的状态
  • 函数对象可作为参数传递

示例如下:

class Myadd
{
public:
	int operator()(int v1, int v2)
	{
		this->count++;
		return v1 + v2;
	}

	int count = 0;//内部自己的状态,不用使用全局变量或静态变量
};

void test01()
{
	Myadd m;
	cout << m(10, 10) << endl;//可以写参数,也可以有返回值,类似普通函数
	cout << m(10, 10) << endl;
	cout << m(10, 10) << endl;
	cout << m(10, 10) << endl;
	cout << "仿函数调用次数:" << m.count << endl;


}

结果如下:
在这里插入图片描述
第三个特点在排序算法中应用广泛,之后的内容会提到这部分,这里就先不讲解了。


3.2 谓词

谓词是指返回bool类型的仿函数,如果operator()接受一个参数,就叫做一元谓词,如果operator()接受两个参数吗,就叫做二元谓词。
一元谓词示例如下:

class great
{
public:
	//一元谓词
	bool operator()(int val)
	{
		return val > 5;
	}
};

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	//查找容器中有没有大于5的数字
	vector<int>::iterator it = find_if(v.begin(), v.end(), great());//创建了一个匿名函数对象
	if (it == v.end())
	{
		cout << "未找到" << endl;
	}
	else
		cout << "找到了" << *it << endl;
}

结果如下:
在这里插入图片描述
二元谓词示例如下:

class great
{
public:
	//二元谓词
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	//使用函数对象 改变算法策略为降序排列
	sort(v.begin(), v.end(), great());//传入匿名对象

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

结果如下:
在这里插入图片描述


3.3 内建函数对象

内建函数对象就是前人为我们创建好的一些仿函数模板,可以拿来直接用的,它们分为三类:

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

用法:这些仿函数所产生的对象,用法和一般函数完全相同,但是使用这些内建函数对象时需要包含头文件< functional >

3.3.1 算术仿函数

在这里插入图片描述
示例如下:

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

	cout << "对50取反得:" << n(50) << endl;
}

//plus 二元仿函数 加法
void test02()
{
	plus<int> p;
	cout << "10+10= " << p(10, 10) << endl;
}

int main() {

	test01();
	test02();

	system("pause");
	return 0;
}

结果如下:
在这里插入图片描述


3.3.2 关系仿函数

在这里插入图片描述
示例如下:

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	//使用关系仿函数,进行降序排列
	sort(v.begin(), v.end(), greater<int>());//传入匿名对象

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

结果如下:
在这里插入图片描述


3.3.3 逻辑仿函数

在这里插入图片描述
逻辑仿函数基本用不到啊,不想讲了,下一个。


4. 常用算法

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

4.1 遍历算法

4.4.1 for_each算法

在这里插入图片描述
for_each函数有三个参数分别为:起始迭代器、终止迭代器、普通函数或仿函数
使用普通函数示例如下:

//普通函数
void print(int v)
{
	cout << v << " ";
}
void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	//for_each算法
	for_each(v.begin(), v.end(), print);
	cout << endl;
	
}

结果如下:
在这里插入图片描述
使用仿函数实例如下:

//仿函数
class P
{
public:
	void operator()(int v)
	{
		cout << v << " ";
	}
};
void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	//for_each算法
	for_each(v.begin(), v.end(), P());
	cout << endl;

	
}

结果如下:
在这里插入图片描述


4.1.2 transform算法

transform算法是将一个容器搬运到另一个容器中,并在这个过程中可以提供一个函数或函数对象,对搬运后的容器进行操作。
transform的5个参数为:原容器起始迭代器、原容器终止迭代器、搬运后容器起始迭代器、函数或函数对象。
示例如下:

//普通函数
void print(int v)
{
	cout << v << " ";
}

//函数对象
class P
{
public:
	int operator()(int v)
	{
		return v+100;
	}
};
void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	vector<int>v2;//目标容器
	v2.resize(v.size());//目标容器需要提前开辟空间

	//transform算法
	transform(v.begin(), v.end(), v2.begin(), P());

	for_each( v2.begin(), v2.end(), print);
	cout << endl;
	
}

结果如下:
在这里插入图片描述


4.2 查找算法

在这里插入图片描述

4.2.1 find算法

示例如下:

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	vector<int>::iterator it = find(v.begin(), v.end(), 5);
	if (it != v.end())
	{
		cout << "找到了" << *it << endl;
	}
	else
	{
		cout << "没找到" << endl;
	}
}

结果如下:
在这里插入图片描述


4.2.2 find_if算法:

示例如下:

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

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	vector<int>::iterator it = find_if(v.begin(), v.end(), great());
	if (it != v.end())
	{
		cout << "找到了" << *it << endl;
	}
	else
	{
		cout << "没找到" << endl;
	}
}

结果如下:
在这里插入图片描述


4.2.3 adjacent_find算法:

示例如下:

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	v.push_back(9);
	v.push_back(10);

	vector<int>::iterator it = adjacent_find(v.begin(), v.end());
	if (it == v.end())
	{
		cout << "没有相邻重复元素" << endl;
	}
	else
	{
		cout << "相邻重复元素为:" << *it << endl;
	}
}

结果如下:
在这里插入图片描述


4.2.4 binary_search算法:

注意:binary_search算法**只能查找有序序列!**如果是无序序列则结果未知。
示例如下:

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	v.push_back(10);

	bool x = binary_search(v.begin(),v.end(), 5);
	if (x)
	{
		cout << "没有该元素" << endl;
	}
	else
	{
		cout << "找到该元素" << endl;
	}
}

结果如下:
在这里插入图片描述


4.2.5 count算法:

示例如下:

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	v.push_back(10);

	bool x = binary_search(v.begin(),v.end(), 5);
	if (!x)
	{
		cout << "没有该元素" << endl;
	}
	else
	{
		cout << "该元素出现的次数为:" << count(v.begin(),v.end(),5) << endl;
	}
}

结果如下:
在这里插入图片描述


4.2.6 count_if算法:

示例如下:

class great
{
public:
	bool operator()(int v)
	{
		return v > 5;
	}
};

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	v.push_back(10);

	int x = count_if(v.begin(),v.end(), great());
	if (!x)
	{
		cout << "没有该元素" << endl;
	}
	else
	{
		cout << "大于5的元素的个数为:" << x << endl;
	}
}

结果如下:
在这里插入图片描述


4.3 排序算法

在这里插入图片描述

4.3.1 sort算法:

sort算法有三个参数:起始迭代器、结束迭代器、谓词
这里的谓词就可以用内建函数对象greater了,但要记得包含头文件< functional >
示例如下:

class great
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void print(int v)
{
	cout << v << " ";
}

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	for_each(v.begin(), v.end(), print);
	cout << endl;

	//默认为升序排序
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), print);
	cout << endl;

	//改为降序排序
	sort(v.begin(), v.end(), great());
	for_each(v.begin(), v.end(), print);
	cout << endl;

}

结果如下:
在这里插入图片描述


4.3.2 random_shuffle算法

random_shuffle算法有两个参数:起始迭代器、结束迭代器,它可以对范围内的元素进行洗牌,随机打乱它们的顺序。
示例如下:

class great
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void print(int v)
{
	cout << v << " ";
}

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	for_each(v.begin(), v.end(), print);
	cout << endl;

	//默认为升序排序
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), print);
	cout << endl;

	//改为降序排序
	sort(v.begin(), v.end(), great());
	for_each(v.begin(), v.end(), print);
	cout << endl;

	//对第二个元素到倒数第二个元素进行洗牌
	random_shuffle(++v.begin(), --v.end());
	for_each(v.begin(), v.end(), print);
	cout << endl;

}

结果如下:
在这里插入图片描述
这样得到的结果不是真正的随机,需要在主函数或test01函数中加一行随机数种子的代码,并包含ctime的头文件。

#include<ctime>
srand((unsigned int)time(NULL));

结果如下:
在这里插入图片描述


4.3.3 merge算法

merge算法可以将两个容器合并,并存储到另一个容器中,但有一个前提就是这两个容器必须是有序的,它有五个参数:容器1的起始迭代器、容器1的结束迭代器、容器2的起始迭代器、容器2的结束迭代器、目标容器起始迭代器。
示例如下:

class great
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void print(int v)
{
	cout << v << " ";
}

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	for_each(v.begin(), v.end(), print);
	cout << endl;
	sort(v.begin(), v.end());

	vector<int> v2;
	for (int i = 0; i < 10; i++)
	{
		v2.push_back(i);
	}

	for_each(v2.begin(), v2.end(), print);
	cout << endl;

	//目标容器
	vector<int>vN;
	//需要提前给目标容器分配内存空间
	vN.resize(v.size() + v2.size());

	merge(v.begin(), v.end(), v2.begin(), v2.end(), vN.begin());
	for_each(vN.begin(), vN.end(), print);
	cout << endl;
}

结果如下:
在这里插入图片描述
注意:在将两个容器合并到目标容器之前需要提前给目标容器分配空间,分配空间的大小为:v1.size() + v2.size()


4.3.4 reverse算法

reverse算法可以将容器内元素进行反转,它有两个参数:起始迭代器和结束迭代器。
示例如下:

class great
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void print(int v)
{
	cout << v << " ";
}

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	cout << "排序前:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;

	cout << "排序后:" << endl;
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), print);
	cout << endl;

	cout << "反转后:" << endl;
	reverse(v.begin(), v.end());
	for_each(v.begin(), v.end(), print);
	cout << endl;

}

结果如下:
在这里插入图片描述


4.4 拷贝和替换算法

在这里插入图片描述

4.4.1 copy算法

copy算法有三个参数:原容器起始迭代器、原容器结尾迭代器、新容器起始迭代器。
注意:拷贝一般都需要提前为目标容器开辟内存空间
示例如下:

void print(int v)
{
	cout << v << " ";
}

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	for_each(v.begin(), v.end(), print);
	cout << endl;

	vector<int>v2;//目标迭代器
	v2.resize(v.size());//需提前为目标容器开辟内存空间
	copy(v.begin(), v.end(), v2.begin());

	for_each(v2.begin(), v2.end(), print);
	cout << endl;

}

结果如下:
在这里插入图片描述
一般在开发中直接使用等号赋值就好了,没必要使用copy算法,但我们也需要知道它的存在和意义。


4.4.2 replace算法

replace算法有四个参数:起始迭代器、结束迭代器、旧元素、新元素。
示例如下:

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	for_each(v.begin(), v.end(), print);
	cout << endl;

	//将v中的40全改为100
	replace(v.begin(), v.end(), 40, 100);
	for_each(v.begin(), v.end(), print);
	cout << endl;
}

结果如下:
在这里插入图片描述


4.4.3 replace_if算法

replace_if算法有四个参数:开始迭代器、结束迭代器、谓词、新元素
示例如下:

class great
{
public:
	bool operator()(int v)
	{
		return v > 40;
	}
};

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	for_each(v.begin(), v.end(), print);
	cout << endl;

	//将v中的大于40的元素全改为100
	replace_if(v.begin(), v.end(), great(), 100);
	for_each(v.begin(), v.end(), print);
	cout << endl;
}

结果如下:
在这里插入图片描述


4.4.4 swap算法

swap算法之前有涉及到,它就是将两个容器内的元素进行互换,那么它的两个参数自然就是两个容器了。
有一点需要注意一下:swap算法交换的两个容器必须是同一种类型
示例如下:

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	vector<int>v2;
	for (int i = 0; i < 10; i++)
	{
		v2.push_back(i);
	}

	cout << "互换前:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;
	for_each(v2.begin(), v2.end(), print);
	cout << endl;

	swap(v, v2);
	cout << "互换后:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;
	for_each(v2.begin(), v2.end(), print);
	cout << endl;
}

结果如下:
在这里插入图片描述


4.5 算数生成算法

在这里插入图片描述

4.5.1 accumulate算法

accumulate算法是用来计算容器元素累计总和的,它可以返回整型或浮点型,它有三个参数:起始迭代器、结束迭代器、起始值。此处的起始值为起始累加值,也就是说它会把这个值也加到容器元素总和内。
示例如下:

void test01()
{
	vector<int>v;
	v.push_back(50);
	v.push_back(40);
	v.push_back(80);
	v.push_back(60);
	v.push_back(51);
	v.push_back(40);
	v.push_back(10);

	for_each(v.begin(), v.end(), print);
	cout << endl;

	int total = accumulate(v.begin(), v.end(), 0);
	cout << "容器中的累计总和为:" << total << endl;
}

结果如下:
在这里插入图片描述


4.5.2 fill算法

fill算法是向指定容器中填充指定元素,它有三个参数:起始迭代器、结束迭代器、填充的值。
示例如下:

void test01()
{
	vector<int>v;
	v.resize(10);

	cout << "填充前:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;

	//将容器中内存空间填充的0改为100
	fill(v.begin(), v.end(), 100);

	cout << "填充后:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;

}

结果如下:
在这里插入图片描述


4.6 集合算法

在这里插入图片描述

4.6.1 set_intersection算法

set_intersection算法有5个参数:容器1的起始迭代器、容器1的结束迭代器、容器2的起始迭代器、容器2的结束迭代器、目标容器的起始迭代器。它的返回值是目标容器的结束迭代器,在遍历目标容器时结束迭代器的位置也需要传入这个返回的迭代器。
有一点需要注意:求交集的两个容器必须都为有序序列
示例如下:

void test01()
{
	vector<int>v;
	vector<int>v2;

	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
		v2.push_back(i + 5);
	}
	cout << "v:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;

	cout << "v2:" << endl;
	for_each(v2.begin(), v2.end(), print);
	cout << endl;

	vector<int>vN;//目标容器
	vN.resize(min(v.size(), v2.size()));

	//获取交集
	vector<int>::iterator it = set_intersection(v.begin(), v.end(), v2.begin(), v2.end(), vN.begin());
	cout << "vN:" << endl;
	for_each(vN.begin(), it, print);
	cout << endl;

}

结果如下:
在这里插入图片描述


4.6.2 set_union算法

set_union算法的参数和求交集的算法一样,返回值也为结束迭代器,在遍历时同样也需要传入这个返回的结束迭代器。
示例如下:

void test01()
{
	vector<int>v;
	vector<int>v2;

	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
		v2.push_back(i + 5);
	}
	cout << "v:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;

	cout << "v2:" << endl;
	for_each(v2.begin(), v2.end(), print);
	cout << endl;

	vector<int>vN;//目标容器
	vN.resize(v.size()+ v2.size());

	//获取并集
	vector<int>::iterator it = set_union(v.begin(), v.end(), v2.begin(), v2.end(), vN.begin());
	cout << "vN:" << endl;
	for_each(vN.begin(), it, print);
	cout << endl;

}

结果如下:
在这里插入图片描述
注意:求并集的两个容器也需要是有序序列。


4.6.3 set_difference算法

set_difference算法的参数和求交集、并集的算法一样,返回值也为结束迭代器,在遍历时同样也需要传入这个返回的结束迭代器。
示例如下:

void test01()
{
	vector<int>v;
	vector<int>v2;

	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
		v2.push_back(i + 5);
	}
	cout << "v:" << endl;
	for_each(v.begin(), v.end(), print);
	cout << endl;

	cout << "v2:" << endl;
	for_each(v2.begin(), v2.end(), print);
	cout << endl;

	vector<int>vN;//目标容器
	vN.resize(max(v.size(),v2,size()));

	//获取差集
	vector<int>::iterator it = set_difference(v.begin(), v.end(), v2.begin(), v2.end(), vN.begin());
	cout << "vN:" << endl;
	for_each(vN.begin(), it, print);
	cout << endl;

}

结果如下:
在这里插入图片描述


C++的基本知识到这里应该基本学完了,我舍友跟我说,他老师写了十多年的C++程序都不敢说自己精通C++,C++是一门深厚的语言,前面的路还很长,我会尽力去探索,继续与C++相爱相杀吧,尽量不打退堂鼓。
下面我可能会写一点Latex、MATLAB或者数学建模的知识,因为开学要打国赛了…ROS可能又得鸽到下学期了…暑假我还参加了个哈工大本部的机器人夏令营,到时候可能也会分享自己的心得和笔记吧。

“祝我天天快乐!”

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值