C++ 标准模板库


一、模板

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

模板就是建立通用的模具, 大大提高复用性
不能直接使用,只是一个框架

函数模板

  • C++ 另一种编程思想称为 泛型编程,主要利用的技术就是模板
  • C++ 提供两种模板机制 函数模板类模板

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

语法:
template
函数声明或定义

解释:
template 声明创建模板
typename 表明其后面的符号是一种数据类型,可以用class 代替
T 通用的数据类型, 名称可以替换,通常为大写字母

我一般函数模板用 typename , 类模板用 class

template<typename T>
void func(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

template<typename T>
void func1()
{
	cout << "func1 调用" << endl;
}

int main()
{
	//自动类型推导
	int a = 20;
	int b = 10;
	func(a, b);

	//	显示指定类型
	func<int>(a, b);
	func1<int>();//必须指定类型,无法进行推导
	
	return 0;
}

函数模板案例-数组排序

//交换值
template<typename T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

//数组排序
template<typename T>
void mySort(T arr[] , int iLen)
{
	for (int i = 0; i < iLen; ++i)
	{
		int iMax = i;
		for (int j = i + 1; j < iLen; ++j)
		{
			if (arr[iMax] < arr[j])
			{
				iMax = j;
			}
		}
		mySwap(arr[iMax], arr[i]);
	}
}

void test03()
{
	char charArr[] = "badcfe";
	int ilen = sizeof(charArr) / sizeof(char);
	mySort<char>(charArr, ilen);
	cout << charArr << endl;
	system("pause");
}

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

  • 普通函数调用时可以发生自动类型转换(隐式转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换

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

template<class NameType, class AgeType = int>
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	
	NameType m_Name;
	AgeType m_Age;
};

Person<string, int> p("孙悟空", 1000);

二、STL的初识

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

1、STL 的诞生

面向对象三大特性:封装,继承,多态==
封装:把属性,行为类似的东西抽象出来,作为一个整体,来实现这些事或物
继承:子类将父类所有的属性,行为拿到手
多态:一个函数名称,有多个接口;由于对象创建的不同,父类指针指向子类对象,调用同一个接口,会产生不同的形态
泛型编程:模板,更通用化

2、STL 的基本概念

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

3、STL六大组件

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

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

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

容器:置物指所也
STL 容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组,链表,树,栈,队列,集合,映射表等
这些容器分为 序列式容器关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置;
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法非质变算法
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找,计数,遍历,寻找极值等等

迭代器:容器与算法之间的粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表达方式。
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

三、常用容器

1、vector 存放内置数据类型

容器:vector
算法:for_each
迭代器:vector::iterator

#include <iostream>
#include <algorithm>//标准算法头文件
#include <vector>
using namespace std;

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

int main()
{
	vector<int> vctiTemp;
	vctiTemp.push_back(10);
	vctiTemp.push_back(20);
	vctiTemp.push_back(30);
	vctiTemp.push_back(40);
	
	vector<int>::iterator itBegin = vctiTemp.begin();//执行容器中第一个元素
	vector<int>::iterator itEnd = vctiTemp.end();//指向容器中最后一个元素的下一个位置
	for(;itBegin != itEnd; ++itBegin )
	{
		cout << *itBegin << endl;
	}
	
	for_each(vctiTemp.begin(), vctiTemp.end(), myPrint)//回调
	{
		;
	}
}

2、vector 存放自定义数据类型

学习目标:vector 中存放自定义数据类型,并打印输出

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

using namespace std;

class Person
{
public:
	Person(string strName, int iAge):m_strName(strName), m_iAge(iAge)
	{
		
	}

	string m_strName;
	int m_iAge;
};

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

	vector<Person>::iterator it = v.begin();
	for (; it != v.end(); ++it)
	{
		cout << (*it).m_strName << "\t" << (*it).m_iAge << endl;
	}

	return;
}

int main()
{
	test01();
	return 0;
}

3、vector 容器嵌套容器

void test02()
{
	vector< vector<int>> vviTemp;

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

	vviTemp.push_back(v1);
	vviTemp.push_back(v2);
	vviTemp.push_back(v3);
	vviTemp.push_back(v4);

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

4、string 容器-构造函数

string 是 C++ 风格的字符串,本质是一个类
与 char* 区别:

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

构造函数:
string();
=>string str;

string(const char* s);

=>const char* str = “hello world”;
string s2(str);

string(const string& str);

=>string s3(s2);

string(int n, char c);

=>string s4(10, ‘a’);

5、string 容器-字符串查找,替换,比较,存取,插入,删除,子串

注意:下标都是从0 号位置开始的

//rfind 和 find区别
//rfind 从右往左查找,find 从左往右查找
string str1 = "abcdefgde";
int ipos = str1.find("de");
if(-1 == ipos)
{
	cout << "未找到字符串" << endl;
}
else
{
	cout << "找到字符串位置" << ipos << endl;
}

//源文:string& replace(int pos, int n, const string& str);//替换从pos 开始n 个字符为字符串str
string str2 = "abcdefg";
str.replace(1, 3, "1111");

//比较 compare 返回值 -1, 0 , 1
字符串1大于字符串2 ,返回值为 1, eg: str1.compare(str2) == 0
字符串1等于字符串2 ,返回值为 0
字符串1小于字符串2 ,返回值为-1

//string 中单个字符存取方式有两种
char& operator[](int n);// 通过[]方式取字符
char& at(int n);//通过at 方式获取字符

//对string 字符串进行插入和删除字符操作
string& insert(int pos, const char* s);//插入字符
string& erase(int pos, int n = npos);//删除从Pos 开始的n个字符

//从字符串中获取想要的子串
string substr(int pos = 0, int n = npos) const;//返回由pos 开始的n个字符组成的字符串

6、set 容器

判断是否插入成功

#include <iostream>
#include <set>
using namespace std;

void test01()
{
	set<int> s;
	pair<set<int>::iterator, bool> ret = s.insert(10);
	if(ret.second)
	{
		cout << "插入成功" << endl;
	}
	else
	{
		cout << "插入失败" << endl;
	}
}

set容器排序

#include <set>
class MyCompare
{
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void test02()
{
	set<int, MyCompare> s1;
	s1.insert(10);
	s1.insert(40);
}

7、map 容器

map 中所有元素都是pair(关联式容器,底层结构是二叉树实现的)
pair 中第一个元素为key (键值),不允许重复,起到索引作用,第二个元素为value (实值)
所有元素都会根据元素的键值自动排序

map<string, string> mapTemp;
mapTemp.insert(make_pair("12", "34"));
if(mapTemp.find("12") != mapTemp.end())//查找容器中字符串是否存在
{
	cout << "找到了" << endl;
}

四、STL-仿函数

又名函数对象
总结:
1、函数对象通常不定义构造函数和析构函数,所以在构造和析构时不会发生任何问题,避免了函数调用的运行时问题
2、函数对象超出普通函数的概念,函数对象可以有自己的状态
3、函数对象可内联编译,性能好,用函数指针几乎不可能
4、模板函数对象使函数对象具有通用性,这也是他的优势之一。

谓词
是指普通函数或 重载的 operation() 返回值 是bool 类型的函数对象(仿函数),如果operator 接受一个参数,那么叫做一元谓词,如果接受两个参数,那就叫二元谓词。

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

本质是一个类,不是一个函数

class MyPrint
{
public:
	MyPrint()
	{
		iCount = 0;
	}
	void Operator()(string strText)
	{
		cout << strText << end;
		this->iCount++;
	}
private:
	int iCount;
}

//查找容器中 有没有大于5的数字

class GreateFive
{
	void Operator()(int iNum)
	{
		return iNum > 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(), GreateFive());
	if(it != v.end())
	{
		cout << "没找到" << endl;
	}
}

五、STL-常用算法

1、常用遍历算法

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

for_each(iterator begin, iterator end, _func) //遍历容器

//普通函数
void Print01(int var)
{
	cout << var << " ";
}

//仿函数
class myPrint02
{
	void operator() (int val)
	{
		cout << val << " ";
	}
};

vector<int> vctIntVar;
//插入数值

for_each(vctIntVar.begin(), vctIntVar.end(), Print01);

for_each(vctIntVar.begin(), vctIntVar.end(), myPrint02());

transform //搬运容器到另一个容器中
函数原型:
transform(iterator beg1, iterator end1, iterator beg2, _func);
//beg1 源容器开始迭代器
//end1 源容器结束迭代器
//beg2 目标容器开始迭代器
//_func 函数或者函数对象

class Transform
{
public:
	int operator()(int v)
	{
		return v;
	}
};

void test01()
{
	vector<int> v;
	for(int i = 0; i < 10; ++i)
	{
		v.push_back(i);
	}
	vector<int> vctTarget;
	vctTarget.resize(v.size());//注意:目标容器需要提前开辟空间
	transform(v.begin(), v.end(), vctTarget.begin(), Transform());
}

2、常用查找算法

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

函数原型:
find(iterator beg, iterator end, value);
find_if(iterator beg, iterator end, _func());
adjacent_find(iterator beg, iterator end);//用迭代器接收位置 eg:vector::iterator pos
bool binary_search(iterator beg, iterator end, value);//查找指定元素是否存在,注意:容器必须是有序的序列
int count(iterator beg, iterator end, value);//计数

若是自定义类型,需要重载 == 底层 find 知道如何对比Person 类型数据

bool operator == (const Person& p)
{
	if (this->m_Name = p.m_Name && this->m_Age == p.m_Age)
	{	
		return true;
	}
	return false;
}

//内置数据类型

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

	//查找 容器中 是否有 5  这个元素
	vector<int>::iterator it = find(v.begin(), v.end(), 5);
	if (it == v.end())
	{
		cout << "没有找到" << endl;
	}
	else
	{
		cout << "找到:" << *it << endl;
	}
}

3、常用排序算法

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

函数原型:
sort(iterator beg, iteratro end, _Pred);

默认升序
sort(v.begin(), v.end());

//改变为降序
sort(iterator beg1, iterator end1, greater());//包含头 #include
random_shuffle(v.begin(), v.end());//随机种子 srand((unsigned int)time(NULL)); 包含头#include
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);//将两个有序的容器,合并到另一个容器中,还是有序的
reverse(iterator beg1, iterator end1);//反转容器顺序

4、常用拷贝和替换算法

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

copy(iterator beg1, iterator end1, iterator dest);
replace(iterator beg1, iterator end1, oldvalue, newvalue);
replace_if(iterator beg1, iterator end1, _pred, newvalue);// -pred 谓词
swap(container c1, container c2);

5、常用算术生成算法

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

函数原型:
int total = accumulate(iterator beg, iterator end, int value);// 头文件 #include, value: 起始累计值
fill(iterator beg, iterator end, int value);//填充

6、常用集合算法

set_intersection //求两个容器的交集
set_union //求两个容器的并集
set_difference //求两个容器的差集

函数原型:
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

拓展
取容器最小值
vector v1;
vector v2;
min(v1.size(), v2.size());

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C葭葭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值