作者前言
🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂
代码模型
- **作者前言**
- 泛型编程
- 函数模板
- 函数模板的实例化
- 隐式实例化
- 显示实例化
- 类模板
- 类的实例化(区别普通类和模板类的类型)
- 类模板声明和定义分离
- 普通函数和函数模板
泛型编程
前面我们学习了c++的函数重载,知道一个函数名可以相同,但是形参的类型或者个数不能相同,如果我们要通过操作不同类型的数据可以进行函数重载,但是很不方便。
进行函数重载,效率也很低下,每次出现新的类型,就要重载一次,
泛型编程的出现就是为了解决这些问题而生的
函数模板
函数模板格式:
template<typename T1, typename T2,…,typename Tn>
返回值类型 函数名(参数列表){}
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
模板参数和函数的参数很像,函数的参数定义的是形参,模板参数定义的是类型
#include<iostream>
using namespace std;
template<typename T1>
void Swa(T1& a, T1& b)
{
T1 c = a;
a = b;
b = c;
}
int main()
{
int a = 10, b = 30;
double c = 1.0, d = 2.0;
Swa(a, b);
Swa(c, d);
cout << "a=" << a << ",b=" << b << endl;
cout << "c=" << c << ",d=" << d << endl;
return 0;
}
不同类型调用的函数不是同一个的,
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此
函数模板的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
隐式实例化
隐式实例化:让编译器根据实参推演模板参数的实际类型
#include<iostream>
using namespace std;
template<typename T1>
void Swa(T1& a, T1& b)
{
T1 c = a;
a = b;
b = c;
}
int main()
{
int a = 10, b = 30;
double c = 1.0, d = 2.0;
Swa(a, b);
Swa(c, d);
cout << "a=" << a << ",b=" << b << endl;
cout << "c=" << c << ",d=" << d << endl;
return 0;
}
如果函数模板参数只有一个,例如Swa(a,c),这个编译器就会报错,隐式实例化,也就是编译器看到我们传入但是参数进行推演,进行确定类型
显示实例化
#include<iostream>
using namespace std;
template<typename T>
T Add( T a, T b)
{
return a+b;
}
template<typename T1>
void print(T1& a)
{
cout << a <<endl;
}
int main()
{
int a = 1.0;
double b = 2.0;
cout<<Add(a,(int)b)<<endl;//少数用法
cout <<Add<int>(3,3.8)<<endl;//显式实例化
return 0;
}
格式:
函数名<类型>(参数列表)
这样可以模板参数只有一个,传参可以传不同的类型
模版参数的如果推不出类型就会报错,通常模版参数一般有几种使用场景
作为函数参数
template<typename T1>
void print(T1& a)
{
cout << a <<endl;
}
我们只需要进行传参就可以知道T的类型,
使用显示实例化
template<typename T1>
void Sta(int n )
{
T1*_a = new T1[n];
int _top = 0;
int _capacity = n;
}
如果我们要推出T1的类型就必须进行显示实例化,例如:
Sta(30)
类模板
#include<iostream>
using namespace std;
template<class T1>
class Stack
{
public:
Stack(int n = 4)
{
_a = new T1[n];
_top = 0;
_capacity = n;
}
Stack(const Stack& a)
{
_top = a._top;
_capacity = a._capacity;
_a = new T1[_capacity];
}
~Stack()
{
delete[] _a;
_top = 0;
_capacity = 0;
}
private:
T1* _a;
int _top;
int _capacity;
};
int main()
{
//显示实例化
Stack<int> str1;
Stack<char> str2;
return 0;
}
模板格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
类的实例化(区别普通类和模板类的类型)
我们知道普通类,一旦定义了就是确定了类型,所以可以理解为普通类的类名就是类型
而类的模板的类名不是类型,我们需要显示实例化才能确定是类,
Stack<int>str1;
Stack<double>str2;
Stack是类型,
Stack 只是类名
可以看到模板类的类型就是这样的,普通类的类型就是类名
类模板声明和定义分离
#include<iostream>
using namespace std;
template<class T1>
class Stack
{
public:
Stack(int n = 4);
Stack(const Stack& a)
{
_top = a._top;
_capacity = a._capacity;
_a = new T1[_capacity];
}
~Stack()
{
cout << _capacity << endl;
delete[] _a;
_a = nullptr;
_top = 0;
_capacity = 0;
}
private:
T1* _a;
int _top;
int _capacity;
};
//定义
template<class T1>
Stack<T1>::Stack(int n)
{
_a = new T1[n];
_top = 0;
_capacity = n;
}
int main()
{
//显示实例化
Stack<int> str1;
Stack<char> str2;
return 0;
}
注意:类模板声明和定义不支持分开到多个文件,只能在一个文件内
普通函数和函数模板
在模板函数和普通函数重载时,C++编译器对函数的调用规则如下:
当函数模板和普通函数都符合调用时,优先选择普通函数。如果想显示使用函数模板,则使用<>类型列表。
如果函数模板能产生更好的匹配,则使用函数模板。
函数模板不允许自动类型转化,普通函数能够进行自动类型转换(隐式类型转换)。
#include<iostream>
using namespace std;
template<typename T>
T Add(const T& a,const T& b)//这里是引用的是一个临时变量,具有常性,需要用const来接收
{
return a + b;
}
int Add(int a, int b)
{
return a + b;
}
int main()
{
cout << Add(1, 2) <<endl;//调用的是普通函数
cout<< Add<double>(2.1, 3.1) <<endl;//调用的也是普通函数
return 0;
}