C++提高编程

*** 本阶段主要针对C++泛型编程STL技术做详细讲解,探讨C++更深层的使用**

模板的概念:建立通用模具,大大提高复用性
C++提供两类模板机制:函数模板和类模板

函数模板
函数声明或定义
template//函数模板的声明或定义

//利用模板提供通用的交换函数
template
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
//函数模板实现交换
1.自动类型实现交换,必须推导出一致的数据类型,才可以使用
int a = 10;
int b = 20;
mySwap(a, b);
2.显示指定类型
mySwap(a,b);
1.在使用自动类型推导,必须推导出一致的数据类型T,才可以使用
2.模板必须确定出T 的数据类型,才可以使用。

template<class T>
void func()
{
	cout << "func 调用" << endl;
}

void test02()
{
	//func(); //错误,模板不能独立使用,必须确定出T的类型
	func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}

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

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换
	//函数模板
	template<class T>
	T myAdd02(T a, T b)  
	{
		return a + b;
	}
	//myAdd02(a, c); // 报错,使用自动类型推导时,不会发生隐式类型转换

	myAdd02<int>(a, c); //正确,如果用显示指定类型,可以发生隐式类型转换
1.2.5 普通函数与函数模板的调用规则

调用规则如下:

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板
void myPrint(int a, int b)
{
	cout << "调用的普通函数" << endl;
}
template<typename T>
void myPrint(T a, T b) 
{ 
	cout << "调用的模板" << endl;
}

template<typename T>
void myPrint(T a, T b, T c) 
{ 
	cout << "调用重载的模板" << endl; 
}
//1、如果函数模板和普通函数都可以实现,优先调用普通函数
	// 注意 如果告诉编译器  普通函数是有的,但只是声明没有实现,或者不在当前文件内实			现,就会报错找不到
	int a = 10;
	int b = 20;
	myPrint(a, b); //调用普通函数

	//2、可以通过空模板参数列表来强制调用函数模板
	myPrint<>(a, b); //调用函数模板

	//3、函数模板也可以发生重载
	int c = 30;
	myPrint(a, b, c); //调用重载的函数模板

	//4、 如果函数模板可以产生更好的匹配,优先调用函数模板
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2); //调用函数模板

既然提供了函数模板,最好就不要提供普通函数,负责会出现二义性

类模板的语法:建立一个通用的类,类中的成员数据可以不具体制定。

#include <string>
//类模板
template<class NameType, class AgeType> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

void test01()
{
	// 指定NameType 为string类型,AgeType 为 int类型
	Person<string, int>P1("孙悟空", 999);
	P1.showPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

类模板与函数模板区别主要有两点:
1.类模板没有自动类型推导的使用方式,因此必须使用显示类型的推导
2.类模板在模板参数列表中可以有默认参数
类模板的成员函数并不是在一开始就创建的,而是在调用时才创建。

class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};

class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show" << endl;
	}
};

template<class T>
class MyClass
{
public:
	T obj;

	//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成

	void fun1() { obj.showPerson1(); }
	void fun2() { obj.showPerson2(); }

};

void test01()
{
	MyClass<Person1> m;
	
	m.fun1();

	//m.fun2();//编译会出错,说明函数调用才会去创建成员函数
}

int main() {

	test01();

	system("pause");

	return 0;
}

类模板实列化的对象,向函数传参的方式有三种
1.指定传入的类型–直接显示对象的数据类型
2.参数模板化–将对象中的参数变为模板进行传递
3,整个类模板化

//类模板
template<class NameType, class AgeType = int> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

//1、指定传入的类型
void printPerson1(Person<string, int> &p) 
{
	p.showPerson();
}
void test01()
{
	Person <string, int >p("孙悟空", 100);
	printPerson1(p);
}

当子类模板碰到继承时,需要注意一下几点:当子类继承的父类是一个模板时,子类在声明的时候,需要指出父类T的类型,如果不指定,编译器无法为子类分配内存,如果想灵活指定父类类型,子类也需要变为类模板。无论哪种情况都需要显示的声明父类的类型。

template<class T>
class Base
{
	T m;
};

//class Son:public Base  //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
{
};
void test01()
{
	Son c;
}

//类模板继承类模板 ,可以用T2指定父类中的T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:
	Son2()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}
};

void test02()
{
	Son2<int, char> child1;
}

类模板的内外实现

//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

**
总结:类模板中成员函数类外实现时,需要加上模板参数列表**
类模板分文件编写:1.直接包含.cpp文件2.将声明和实现写到同一个文件中并更改后缀名为.hpp。

类模板和友元:建议全局函数做类内实现,用法简单,并且可以让编译器直接识别。

STL只要分为六大类,重点学习了容器,算法,迭代器,仿函数。

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
  2. 算法:各种常用的算法,如sort、find、copy、for_each等
  3. 迭代器:扮演了容器与算法之间的胶合剂。
  4. 仿函数:行为类似函数,可作为算法的某种策略。

常用容器:
vector嵌套容器:

#include <vector>

//容器嵌套容器
void test01() {

	vector< vector<int> >  v;

	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	vector<int> v4;

	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);
	}

	//将容器元素插入到vector v中
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);


	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {

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

}

stirng管理char* 所分配的内存,不用担心复制和取值越界等,由内部进行管理。
构造函数原型:


* `string();`          				//创建一个空的字符串 例如: string str;
* string(const char* s);`	        //使用字符串s初始化
* `string(const string& str);`    //使用一个string对象初始化另一个string对象
* `string(int n, char c);`           //使用n个字符c初始化

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赋给当前字符串

**总结:**string的赋值方式很多,operator= 这种方式是比较实用的
string字符串拼接


* `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个字符连接到字符串结尾

string的查找和替换


* `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) const;`      //查找str最后一次位置,从pos开始查找
* `int rfind(const char* s, int pos = npos) const;`              //查找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

总结:

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

字符串比较:

* `int compare(const string &s) const; `  //与字符串s比较
* `int compare(const char *s) const;`      //与字符串s比较

字符串的存取

* `char& operator[](int n); `     //通过[]方式取字符
* `char& at(int n);   `                    //通过at方法获取字符

字符串的插入和删除

* `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个字符 

总结:插入和删除的下标都是从0开始
字符串的截取

* `string substr(int pos = 0, int n = npos) const;`   //返回由pos开始的n个字符组成的字符串

vector容器–单端数组–支持随机访问
vector和数组的区别:vector可以动态扩展,动态扩展不是处于数组的静态空间,而是寻找更大的数组空间,然后将原数据拷贝到新空间,释放原空间。

创建vector容器


* `vector<T> v; `               		     //采用模板实现类实现,默认构造函数
* `vector(v.begin(), v.end());   `       //将v[begin(), end())区间中的元素拷贝给本身。
* `vector(n, elem);`                            //构造函数将n个elem拷贝给本身。
* `vector(const vector &vec);`         //拷贝构造函数。

vector容器进行赋值


* `vector& operator=(const vector &vec);`//重载等号操作符


* `assign(beg, end);`       //将[beg, end)区间中的数据拷贝赋值给本身。
* `assign(n, elem);`        //将n个elem拷贝赋值给本身。

vector容量和大小

* `empty(); `                            //判断容器是否为空

* `capacity();`                      //容器的容量

* `size();`                              //返回容器中元素的个数

* `resize(int num);`             //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。

* `resize(int num, elem);`  //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除

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();`   

vector数据存取


* `at(int idx); `     //返回索引idx所指的数据
* `operator[]; `       //返回索引idx所指的数据
* `front(); `            //返回容器中第一个数据元素
* `back();`              //返回容器中最后一个数据元素

vector互换容器

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

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

vector预留空间
减少vector在动态扩展容量的扩展次数


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

deque–双端数组可以对头端进行插入删除

deque与vector区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对而言,对头部的插入删除速度回比vector快
  • vector访问元素时的速度会比deque快,这和两者内部实现有关
    deque的工作原理:deque内部有一个中控器,维护每段缓冲区的内容,缓冲区存放真实数据,中控器维护的是每个缓冲区的地址,使得队列存放一片连续的内存空间。

deque的构造函数:

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

deque的赋值操作

 `deque& operator=(const deque &deq); `         //重载等号操作符


* `assign(beg, end);`                                           //将[beg, end)区间中的数据拷贝赋值给本身。
* `assign(n, elem);`                                             //将n个elem拷贝赋值给本身。

deque的大小操作:

`deque.empty();`                       //判断容器是否为空

* `deque.size();`                         //返回容器中元素的个数

* `deque.resize(num);`                //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。

* `deque.resize(num, elem);`     //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
  deque容器没有capacity的限制它可以向后向前无限制的扩容

deuqe的插入删除

两端插入操作:

- `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位置的数据,返回下一个数据的位置。

deque的数据存取

- `at(int idx); `     //返回索引idx所指的数据
- `operator[]; `      //返回索引idx所指的数据
- `front(); `            //返回容器中第一个数据元素
- `back();`              //返回容器中最后一个数据元素

deque排序


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

使用sort排序算法需要包含头文件algorithm

stack:一种数据结构是一种先进先出的数据结构,栈只允许顶端元素才能被外界使用,因此栈不允许遍历。
栈对外的常用接口

构造函数:

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

赋值操作:

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

数据存取:

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

大小操作:

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

queue容器:是一种先进先出的数据结构。队列只有对头和队尾才可以外界使用,因此不允许有遍历行为。
queue常用的接口:

构造函数:

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

赋值操作:

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

数据存取:

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

大小操作:

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

list容器–是一种物理存储单元上的非连续的存储结构,数据元素的逻辑是通过链表实现的,链表是由一系列结点组成,结点一个是数据元素的数据域,另一个是存储结点的指针域,链表是双向循环链表。链表的存储方式不是连续的存储空间,因此链表List的迭代器只支持前移和后移。
List的优点:
采用动态存储分配,不会造成内存浪费和溢出

  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
    List的缺点:链表灵活,但是指针域和时间遍历额外耗费内存大
    List 的构造函数:
* `list<T> lst;`                               //list采用采用模板类实现,对象的默认构造形式:
* `list(beg,end);`                           //构造函数将[beg, end)区间中的元素拷贝给本身。
* `list(n,elem);`                             //构造函数将n个elem拷贝给本身。
* `list(const list &lst);`            //拷贝构造函数。

list的赋值交换


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

list的大小操作:


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

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值匹配的元素。

list数据存取:


* `front();`        //返回第一个元素。
* `back();`         //返回最后一个元素。

list反转和排序:

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

set/multiset
set容器–底层是二叉树实现的,属于关联式容器
set和multiset区别

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

* `set<T> st;`                        //默认构造函数:
* `set(const set &st);`       //拷贝构造函数

赋值:

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

set容器插入数据时使用insert元素会自动排序

set大小和交换

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

set的插入和删除

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

set查找和统计–对set容器进行查找和统计数据

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

multiset

**区别:**

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

pair对组的创建–队出现的数据,利用数组可以返回两个数据

* `pair<type, type> p ( value1, value2 );`
* `pair<type, type> p = make_pair( value1, value2 );`

set对容器默认数据排序规则为从小到大,利用仿函数可以改变排序规则。仿函数:仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。
set自定义排序数据类型:

#include <set>
#include <string>

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;

};
class comparePerson
{
public:
	bool operator()(const Person& p1, const Person &p2)
	{
		//按照年龄进行排序  降序
		return p1.m_Age > p2.m_Age;
	}
};

void test01()
{
	set<Person, comparePerson> s;

	Person p1("刘备", 23);
	Person p2("关羽", 27);
	Person p3("张飞", 25);
	Person p4("赵云", 21);

	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);

	for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}
int main() {

	test01();

	system("pause");

	return 0;
}

map/multimap容器
map基本概念:
map中所有元素都是pair,pair中第一个元素为key,第二个元素为value,所有元素都会根据key值进行自动排序。
优点:利用key值可以快速找到value

map和multimap区别

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

map容器数据结构和赋值操作


* `map <T1, T2> mp;`                     //map默认构造函数: 
* `map(const map &mp);`             //拷贝构造函数

**赋值:**

* `map& operator=(const map &mp);`    //重载等号操作符

map大小和互换

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

map插入和删除

- `insert(elem);`           //在容器中插入元素。
- `clear();`                    //清除所有元素
- `erase(pos);`              //删除pos迭代器所指的元素,返回下一个元素的迭代器。
- `erase(beg, end);`    //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
- `erase(key);`            //删除容器中值为key的元素。
	//插入
	map<int, int> m;
	//第一种插入方式
	m.insert(pair<int, int>(1, 10));
	//第二种插入方式
	m.insert(make_pair(2, 20));
	//第三种插入方式
	m.insert(map<int, int>::value_type(3, 30));
	//第四种插入方式
	m[4] = 40; **

map的查找和统计

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

map容器技术排序:map容器默认排序规则为按照key值进行,从小到大排序,掌握如何改变排序规则。

–利用仿函数,可以改变排序规则

class MyCompare {
public:
	bool operator()(int v1, int v2) {
		return v1 > v2;
	}
};
void test01() 
{
	//默认从小到大排序
	//利用仿函数实现从大到小排序
	map<int, int, MyCompare> m;

	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(4, 40));
	m.insert(make_pair(5, 50));

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

STL–函数对象
函数对象概念:* 重载函数调用操作符的类,其对象常称为函数对象

  • 函数对象使用重载的()时,行为类似函数调用,也叫仿函数
    函数对象是一个类,不是一个函数
    函数对象使用特点:
    1.函数对象在使用时,可以像普通函数那样调用可以有自己的参数有自己的返回值。
    2.函数对象超出普通函数的概念,函数对象可以有自己的状态。
    3.函数对象可以有自己的参数
//1、函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
class MyAdd
{
public :
	int operator()(int v1,int v2)
	{
		return v1 + v2;
	}
};

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");
	cout << "myPrint调用次数为: " << myPrint.count << endl;
}

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

void test03()
{
	MyPrint myPrint;
	doPrint(myPrint, "Hello C++");
}

int main() {

	//test01();
	//test02();
	test03();

	system("pause");

	return 0;
}

谓词

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

STL内建函数对象 分类:算术仿函数,关系仿函数,逻辑仿函数
如果使用内建函数对象需要引入头文件‘#include’

算术仿函数:实现四则运算,其中negate是一元运算,其他都是二元运算


* `template<class T> T plus<T>`                //加法仿函数
* `template<class T> T minus<T>`              //减法仿函数
* `template<class T> T multiplies<T>`    //乘法仿函数
* `template<class T> T divides<T>`         //除法仿函数
* `template<class T> T modulus<T>`         //取模仿函数
* `template<class T> T negate<T>`           //取反仿函数
#include <functional>
//negate
void test01()
{
	negate<int> n;
	cout << n(50) << endl;
}

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

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

关系仿函数–实现关系对比

* `template<class T> bool equal_to<T>`                    //等于
* `template<class T> bool not_equal_to<T>`            //不等于
* `template<class T> bool greater<T>`                      //大于
* `template<class T> bool greater_equal<T>`          //大于等于
* `template<class T> bool less<T>`                           //小于
* `template<class T> bool less_equal<T>`               //小于等于
#include <functional>
#include <vector>
#include <algorithm>

class MyCompare
{
public:
	bool operator()(int v1,int v2)
	{
		return v1 > v2;
	}
};
void test01()
{
	vector<int> v;

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

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

	//自己实现仿函数
	//sort(v.begin(), v.end(), MyCompare());
	//STL内建仿函数  大于仿函数
	sort(v.begin(), v.end(), greater<int>());

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

int main() {

	test01();

	system("pause");

	return 0;
}

逻辑仿函数使用较少,了解即可。

stl–常用算法
算法主要是由头文件<algorithm> <functional> <numeric>组成。

  • <algorithm>是所有STL头文件中最大的一个,范围涉及到比较、 交换、查找、遍历操作、复制、修改等等
  • <numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数
  • <functional>定义了一些模板类,用以声明函数对象。
    算法简介:
* `for_each`     //遍历容器
* `transform`   //搬运容器到另一个容器中
  • for_each(iterator beg, iterator end, _func);

    // 遍历算法 遍历容器元素

    // beg 开始迭代器

    // end 结束迭代器

    // _func 函数或者函数对象

#include <algorithm>
#include <vector>

//普通函数
void print01(int val) 
{
	cout << val << " ";
}
//函数对象
class print02 
{
 public:
	void operator()(int val) 
	{
		cout << val << " ";
	}
};

//for_each算法基本用法
void test01() {

	vector<int> v;
	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;
}
  • transform(iterator beg1, iterator end1, iterator beg2, _func);

//beg1 源容器开始迭代器

//end1 源容器结束迭代器

//beg2 目标容器开始迭代器

//_func 函数或者函数对象

#include<vector>
#include<algorithm>

//常用遍历算法  搬运 transform

class TransForm
{
public:
	int operator()(int val)
	{
		return val;
	}

};

class MyPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

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

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

	transform(v.begin(), v.end(), vTarget.begin(), TransForm());

	for_each(vTarget.begin(), vTarget.end(), MyPrint());
}

int main() {

	test01();

	system("pause");

	return 0;
}

总结搬运容器需要提前指定预留开辟空间,否则无法正常搬运。

常用的查找算法

- `find`                     //查找元素
- `find_if`               //按条件查找元素
- `adjacent_find`    //查找相邻重复元素
- `binary_search`    //二分查找法
- `count`                   //统计元素个数
- `count_if`             //按条件统计元素个数
  • find(iterator beg, iterator end, value);

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

    // beg 开始迭代器

    // end 结束迭代器

    // value 查找的元素

#include <algorithm>
#include <vector>
#include <string>
void test01() {

	vector<int> v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i + 1);
	}
	//查找容器中是否有 5 这个元素
	vector<int>::iterator it = find(v.begin(), v.end(), 5);
	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_Name == p.m_Name && this->m_Age == p.m_Age) 
		{
			return true;
		}
		return false;
	}

public:
	string m_Name;
	int m_Age;
};

void test02() {

	vector<Person> 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);

	vector<Person>::iterator it = find(v.begin(), v.end(), p2);
	if (it == v.end()) 
	{
		cout << "没有找到!" << endl;
	}
	else 
	{
		cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}
  • find_if(iterator beg, iterator end, _Pred);

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

    // beg 开始迭代器

    // end 结束迭代器

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

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

void test01() {

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

	vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
	if (it == v.end()) {
		cout << "没有找到!" << endl;
	}
	else {
		cout << "找到大于5的数字:" << *it << endl;
	}
}
//自定义数据类型
class Person {
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
public:
	string m_Name;
	int m_Age;
};

class Greater20
{
public:
	bool operator()(Person &p)
	{
		return p.m_Age > 20;
	}

};

void test02() {

	vector<Person> 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);

	vector<Person>::iterator it = find_if(v.begin(), v.end(), Greater20());
	if (it == v.end())
	{
		cout << "没有找到!" << endl;
	}
	else
	{
		cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

总结:find_if按条件查找使查找更加灵活,提供的仿函数可以改变不同的策略

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

    // beg 开始迭代器

    // end 结束迭代器

#include <algorithm>
#include <vector>

void test01()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(5);
	v.push_back(2);
	v.push_back(4);
	v.push_back(4);
	v.push_back(3);

	//查找相邻重复元素
	vector<int>::iterator it = adjacent_find(v.begin(), v.end());
	if (it == v.end()) {
		cout << "找不到!" << endl;
	}
	else {
		cout << "找到相邻重复元素为:" << *it << endl;
	}
}
  • bool binary_search(iterator beg, iterator end, value);

    // 查找指定的元素,查到 返回true 否则false

    // 注意: 在无序序列中不可用

    // beg 开始迭代器

    // end 结束迭代器

    // value 查找的元素

#include <algorithm>
#include <vector>

void test01()
{
	vector<int>v;

	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	//二分查找
	bool ret = binary_search(v.begin(), v.end(),2);
	if (ret)
	{
		cout << "找到了" << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}
}

int main() {

	test01();

	system("pause");

	return 0;
}

二分查找的效率很低,查找容器中的元素必须是有序序列

  • count(iterator beg, iterator end, value);

    // 统计元素出现次数

    // beg 开始迭代器

    // end 结束迭代器

    // value 统计的元素

#include <algorithm>
#include <vector>

//内置数据类型
void test01()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(4);
	v.push_back(5);
	v.push_back(3);
	v.push_back(4);
	v.push_back(4);

	int num = count(v.begin(), v.end(), 4);

	cout << "4的个数为: " << num << 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 test02()
{
	vector<Person> v;

	Person p1("刘备", 35);
	Person p2("关羽", 35);
	Person p3("张飞", 35);
	Person p4("赵云", 30);
	Person p5("曹操", 25);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
    
    Person p("诸葛亮",35);

	int num = count(v.begin(), v.end(), p);
	cout << "num = " << num << endl;
}
int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

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

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

    // 按条件统计元素出现次数

    // beg 开始迭代器

    // end 结束迭代器

    // _Pred 谓词

#include <algorithm>
#include <vector>

class Greater4
{
public:
	bool operator()(int val)
	{
		return val >= 4;
	}
};

//内置数据类型
void test01()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(4);
	v.push_back(5);
	v.push_back(3);
	v.push_back(4);
	v.push_back(4);

	int num = count_if(v.begin(), v.end(), Greater4());

	cout << "大于4的个数为: " << num << endl;
}

//自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;
};

class AgeLess35
{
public:
	bool operator()(const Person &p)
	{
		return p.m_Age < 35;
	}
};
void test02()
{
	vector<Person> v;

	Person p1("刘备", 35);
	Person p2("关羽", 35);
	Person p3("张飞", 35);
	Person p4("赵云", 30);
	Person p5("曹操", 25);

	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(), AgeLess35());
	cout << "小于35岁的个数:" << num << endl;
}


int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

常用排序算法:

- `sort`             //对容器内元素进行排序
- `random_shuffle`   //洗牌   指定范围内的元素随机调整次序
- `merge `           // 容器元素合并,并存储到另一容器中
- `reverse`       // 反转指定范围的元素
  • sort(iterator beg, iterator end, _Pred);

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

    // beg 开始迭代器

    // end 结束迭代器

    // _Pred 谓词

#include <algorithm>
#include <vector>

void myPrint(int val)
{
	cout << val << " ";
}

void test01() {
	vector<int> 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<int>());
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

random_shuffle(iterator beg, iterator end);

// 指定范围内的元素随机调整次序

// beg 开始迭代器

// end 结束迭代器

#include <algorithm>
#include <vector>
#include <ctime>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

void test01()
{
	srand((unsigned int)time(NULL));//定义随机数种子
	vector<int> v;
	for(int i = 0 ; i < 10;i++)
	{
		v.push_back(i);
	}
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	//打乱顺序
	random_shuffle(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}
  • merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // 容器元素合并,并存储到另一容器中

    // 注意: 两个容器必须是有序的,有序的顺序是一致的

    // beg1 容器1开始迭代器
    // end1 容器1结束迭代器
    // beg2 容器2开始迭代器
    // end2 容器2结束迭代器
    // dest 目标容器开始迭代器

#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

void test01()
{
	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());
	for_each(vtarget.begin(), vtarget.end(), myPrint());
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

总结:merge容器合并必须是有序的。

  • reverse(iterator beg, iterator end);

    // 反转指定范围的元素

    // beg 开始迭代器

    // end 结束迭代器

#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

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

	cout << "反转前: " << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	cout << "反转后: " << endl;

	reverse(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

copy:容器内指定元素拷贝到另外一个容器。

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

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

    // beg 开始迭代器

    // end 结束迭代器

    // dest 目标起始迭代器

#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i + 1);
	}
	vector<int> v2;
	v2.resize(v1.size());
	copy(v1.begin(), v1.end(), v2.begin());

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

int main() {

	test01();

	system("pause");

	return 0;
}
  • replace(iterator beg, iterator end, oldvalue, newvalue);

    // 将区间内旧元素 替换成 新元素

    // beg 开始迭代器

    // end 结束迭代器

    // oldvalue 旧元素

    // newvalue 新元素

#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

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

	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	//将容器中的20 替换成 2000
	cout << "替换后:" << endl;
	replace(v.begin(), v.end(), 20,2000);
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}
  • replace_if(iterator beg, iterator end, _pred, newvalue);

    // 按条件替换元素,满足条件的替换成指定元素

    // beg 开始迭代器

    // end 结束迭代器

    // _pred 谓词

    // newvalue 替换的新元素

#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

class ReplaceGreater30
{
public:
	bool operator()(int val)
	{
		return val >= 30;
	}

};

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

	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	//将容器中大于等于的30 替换成 3000
	cout << "替换后:" << endl;
	replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}
  • swap(container c1, container c2);

    // 互换两个容器的元素

    // c1容器1

    // c2容器2

#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

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

	cout << "交换后: " << endl;
	swap(v1, v2);
	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;
}

常用算术生成算法–属于小型生成算法包含在头文件“#include”
算法简介:

  • accumulate // 计算容器元素累计总和

  • fill // 向容器中添加元素

accumulate(iterator beg, iterator end, value);

// 计算容器元素累计总和

// beg 开始迭代器

// end 结束迭代器

// value 起始值

#include <numeric>
#include <vector>
void test01()
{
	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;
}

int main() {

	test01();

	system("pause");

	return 0;
}
  • fill(iterator beg, iterator end, value);

    // 向容器中填充元素

    // beg 开始迭代器

    // end 结束迭代器

    // value 填充的值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值