文章目录
1、函数模版
模版是c++参数多态的工具,模版一般有类模板和函数模版,使得类中的某些数据或者函数成员可以支持任意类型的参数。同时返回值也是一样的,可以取得任意类型。
通过使用模版:
能够使程序员编写和类型无关的代码,比如下面编写一个经典的交换代码,默认的话我们需要指定参数类型,例如整形,那我们就只能对整数类型进行交换,这样我们如果要对浮点数进行交换就很蛮烦,使用模版就可以实现这个与类型无关。
1、函数模版的概念
函数模版的使用在上面已经介绍过了,这里不在赘述,下面是函数模版的基本语法:
template<typename T>
函数声明或定义
说明如下:
- template — 声明创建模板
- typename — 表面其后面的符号是一种数据类型,可以用class代替
- T — 通用的数据类型,名称可以替换,通常为大写字母
2、函数模版例子
下面用一个交换函数来用模版的方式来进行表达:
代码如下:
template<typename T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
使用模版有两种方式可以进行,如下所示:
使用模版进行比较效果如下:
当然这里还是要相同类型的参数进行比较,不然还是会报错的:
因此可以看出,使用函数模版的优势在于:
- 函数模板利用关键字 template
- 使用函数模板有两种方式:自动类型推导、显示指定类型
- 模板的目的是为了提高复用性,将类型参数化
3、使用函数模版来实现多种排序
下面来使用函数模版实现不同类型数据的排序,这里还是用最经典的冒泡排序的方式来实现,根据之前介绍的冒泡排序需要我们进行逐一的筛选然后替换,所以这里需要两个函数模版,如下所所示:
代码如下:
template<typename T>
void mySwap(T &a, T&b)
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void mySort(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
int max = i; //最大数的下标
for (int j = i + 1; j < len; j++)
{
if (arr[max] < arr[j])
{
max = j;
}
}
if (max != i) //如果最大数的下标不是i,交换两者
{
mySwap(arr[max], arr[i]);
}
}
}
下面是测试的函数:
运行结果如下所示:
2、类模板
1、类模板的概念
类模板的作用为:
- 建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表。
使用类模板的语法如下所示:
template<typename T>
类
参数说明如下:
- template — 声明创建模板
- typename — 表面其后面的符号是一种数据类型,可以用class代替
- T — 通用的数据类型,名称可以替换,通常为大写字母
2、类模板使用示例
下面是一个类模板的使用示例,如下所示:
代码如下:
template<typename NameType, typename 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;
};
指定参数类型,调用这个模版
运行结果如下所示:
3、类模板和函数模版的区别
这里类模板与函数模板区别主要有两点:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
4、类模板中成员函数的创建时机
类模板中成员函数的规则如下所示,相比普通类中而言:
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
下面是一个示例:
下面是使用过程的影响,这里如果调用第二个成员函数就会报错
这是因为:类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
代码如下:
#include<iostream>
using namespace std;
#include<string>
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;
}
5、类模板对象做函数参数
类模板实例化出的对象,向函数传参一共有三种传入方式:
- 指定传入的类型 — 直接显示对象的数据类型
- 参数模板化 — 将对象中的参数变为模板进行传递
- 整个类模板化 — 将这个对象类型 模板化进行传递
下面举例来表述这个方式,首先还是先新建一个类模版:
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;
};
类模板进行函数参数的传递一共有三种方式,如下所示:
- 指定参数类型和参数名称直接传入(这种跟普通的参数传入没什么区别):
- 将参数模版化传入
- 直接将整个类模版化
一般第一种比较常用,和普通的传入参数的方法也是一样的。
3、STL
1、STL的基本概念
主要是为了避免重复造轮子的繁琐工作才会产生STL这样的方法,C++的面向对象和泛型编程思想,目的就是复用性的提升,大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作,因此为了建立数据结构和算法的一套标准,诞生了STL。
2、STL介绍
STL从大的框架上主要包含:
- STL(Standard Template Library,标准模板库)
- STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator)
- 容器和算法之间通过迭代器进行无缝连接。
- STL 几乎所有的代码都采用了模板类或者模板函数
STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
- 算法:各种常用的算法,如sort、find、copy、for_each等
- 迭代器:扮演了容器与算法之间的胶合剂。
- 仿函数:行为类似函数,可作为算法的某种策略。
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
- 空间配置器:负责空间的配置与管理。
STL中容器、算法、迭代器
- STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组, 链表,树, 栈, 队列, 集合, 映射表 等
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
- 有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法和非质变算法。
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
- 迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
迭代器种类:
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,支持++、==、!= |
输出迭代器 | 对数据的只写访问 | 只写,支持++ |
前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
双向迭代器 | 读写操作,并能向前和向后操作 | 读写,支持++、–, |
随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、–、[n]、-n、<、<=、>、>= |
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
3、容器
下面来通过示例查看这三个比较重要概念的额应用情况,这三者的名称如下所示:
容器: vector
算法: for_each
迭代器: vector<int>::iterator
下面举一个容器中输入数据然后遍历输出的例子,可以看出其实这个容器还是很像一个数组的,或者python中的列表。
输出结果如下:
代码如下
#include <vector>
#include <algorithm>
void MyPrint(int val)
{
cout << val << endl;
}
void test01()
{
vector<int> v;//创建一个容器
//向容器中放数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
vector<int>::iterator pBegin = v.begin();//获取元素开头和结尾方便后续使用
vector<int>::iterator pEnd = v.end();
cout << "第一种遍历" << endl;
while (pBegin != pEnd)
{
cout << *pBegin << endl;
pBegin++;
}
cout << "第二种遍历" << endl;
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << endl;
}
cout <<"第三种遍历"<< endl;
for_each(v.begin(), v.end(), MyPrint);
}
int main() {
test01();
system("pause");
return 0;
}