前情回顾
在上一层中,我遇到了面向对象的最后一个特性——多态,使用多态可以让代码组织结构更加情绪,使可读性变强,利于程序的前期和后期的扩展和维护,掌握多态之后,面向对象结束了, 后面会是什么呢…
🚄上章地址:第七层:多态
模板
上到了第八层,映入眼帘的是一个巨大的石碑,“你来了,在C++中,除了面向过程编程和面向对象编程以外,还有一种编程思想,叫做泛型编程,泛型编程主要通过模板实现,这层就需要你掌握模板的力量…"。
模板的概念
模板就是建立一个通用的模具,大大提高复用性,比如PPT模板、空白的学生证,这些东西都有一些特点,不能直接使用,需要使用者去添加一些东西,而且不是通用的,比如这个PPT模板是做年度总结的,那就不能强套到一些和年度总结不着边的东西上,因此可以总结出模板的特点。
模板的特点
1.模板只是一个框架
2.这个框架不是万能的
模板分类
在C++中模板分为两类:
函数模板
类模板
函数模板
作用
建立一个通用函数,其函数的返回类型和形参可以不具体制定,用一个虚拟的类型来代表
语法
函数模板的语法为:
templatte< typename T>
函数的声明或者定义
解释:
template:声明创建函数模板
typename:表明其后面的符号是一种通用的数据类型,可以用class替代。
T:通用的数据类型,可以替换成别的字母
可以尝试写一个函数模板:
#include<iostream> using namespace std; template<typename T> void print(T a) { cout << a << endl; } int main() { return 0; }
可以正常跑起来,那函数模板怎么使用呢?
函数模板的使用
1.自动类型推导:直接传入类型,让编译器自己识别是什么类型
2.显示制定类型:告诉编译器是什么类型,语法如下:
函数名< 是什么数据类型 >(参数)
验证一下两种使用方法:
自动类型推导
#include<iostream> using namespace std; template<typename T> void print(T a) { cout << a << endl; } void test1() { print(1); } int main() { test1(); return 0; }
显示指定类型
#include<iostream> using namespace std; template<typename T> void print(T a) { cout << a << endl; } void test1() { print<int>(1); } int main() { test1(); return 0; }
注意事项
1.自动类型推导必须推导出一致的数据类型才可以使用
2.模板必须确定出T的数据类型,才可以使用
第一点解释:
#include<iostream> using namespace std; template<typename T> void print(T a,T b) { cout << a << endl; } void test1() { int a = 0; char b = 0; print(a,b); } int main() { test1(); return 0; }
可以看到,一个int的类型和一个char类型的数据,两个类型就不一致,这个时候,T不知道自己应该是什么,所以报错,说明对于自动类型推导就需要让两个类型一致,那显示指定类型呢?
#include<iostream> using namespace std; template<typename T> void print(T a,T b) { cout << a << b << endl; } void test1() { int a = 0; char b = 0; print<int>(a,b); } int main() { test1(); return 0; }
显示指定类型就可以使用。
第二点解释:
当函数模板没有参数的时候,可以直接调用吗?
#include<iostream> using namespace std; template<typename T> void print() { cout << "hello world" << endl; } void test1() { print(); } int main() { test1(); return 0; }
是不可以直接调用的,因为使用函数模板时,必须人编译器知道通用数据类型是什么,就算没有参数,也需要,这个时候可采用显示指定类型,不管是什么类型,都能跑去来:
#include<iostream> using namespace std; template<typename T> void print() { cout << "hello world" << endl; } void test1() { print<int>(); print<char>(); print<double>(); } int main() { test1(); return 0; }
普通函数和函数模板的区别
普通函数调用可以发生自动类型转换(隐式类型转换)
函数模板在调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型,可以发生隐式类型转换
普通函数:
#include<iostream> using namespace std; int Swap(int a, int b) { return a + b; } void test1() { int a = 20; char b = '1'; int c=Swap(a, b); cout << c << endl; } int main() { test1(); return 0; }
c是69,这是因为发生了隐式类型转换,char转换成了int,同时这是字符1的ASCII码值为49,49加20,就是69。
函数模板:
自动类型推导在上面的注意事项说到,必须推导出统一的数据类型才能跑起来,所以这个时候传两个类型不同的一定是会报错的,就不进行验证了,这个时候试着用显示指定类型试试:
#include<iostream> using namespace std; template<typename T> int Swap(T a, T b) { return a + b; } void test1() { int a = 20; char b = '1'; int c=Swap<int>(a, b); cout << c << endl; } int main() { test1(); return 0; }
这个时候,编译器会将参数内的所有数视为int类型的,就发生了隐式类型转换。
普通函数和函数模板的调用规则
1.如果函数模板和普通函数都可以实现时,优先调用普通函数
2.可以通过空模板参数来强调函数模板,让使用函数模板
3.函数模板可以发生重载
4。如果函数模板可以产生更好的匹配,就使用函数模板
优先调用普通函数
#include<iostream> using namespace std; template<typename T> int Add(T a, T b) { cout << "函数模板调用" << endl; return a + b; } int Add(int a, int b) { cout << "普通函数调用" << endl; return a + b; } void test1() { int a = 20; int b = 20; int c=Add(a, b); cout << c << endl; } int main() { test1(); return 0; }
可以发现两个函数实现内容相同,但是优先调用的普通函数。
空模板强调函数模板
空模板的语法:函数名<>(传参)
#include<iostream> using namespace std; template<typename T> int Add(T a, T b) { cout << "函数模板调用" << endl; return a + b; } int Add(int a, int b) { cout << "普通函数调用" << endl; return a + b; } void test1() { int a = 20; int b= 20; int c=Add<>(a, b); cout << c << endl; } int main() { test1(); return 0; }
这个时候就调用了函数模板了,就是空模板起的作用,可以强制调用模板。
函数模板可以发生重载
#include<iostream> using namespace std; template<typename T> int Add(T a, T b) { return a + b; } template<typename T> int Add(T a, T b, T c) { return a + b + c; } void test1() { int a = 20; int b= 20; int c = 20; int d = Add<>(a, b, c); cout << d << endl; } int main() { test1(); return 0; }