目录
🍉一:什么是模板
🍎二:普通模板的定义
🍍三:类模板的定义
🍌四:模板的实例化
🍇1.当普通模板定义存在可修改返回值产生的分歧
🍈2:类模板实例化声明所需要的注意事项
🥝Tips:声明类成员函数模板的书写形式
在C++当中模板可以说是一个很新的东西。因为我们之前根本就没有见过模板,甚至在C语言当中连听都没有听过。所以我们今天就来介绍一下什么是模板以及模板应该怎么使用的。
一:什么是模板
模板是在C++当中经过引入了函数的重载之后所诞生的一种新的功能。想要详细的了解模板的好处就需要从函数重载的例子开始说起。我们可以来看下面的一段代码:
#include<iostream>
//根据函数重载的例子来了解模板的好处
void swap(int& a, int& b) //交换函数1
{
int tmp = a;
a = b;
b = tmp;
}
void swap(double& a, double& b) //函数重载2
{
double tmp = a;
a = b;
b = tmp;
}
void swap(char& a, char& b) //函数重载3
{
char tmp = a;
a = b;
b = tmp;
}
//写一个模板
int main()
{
int number1 = 12, number2 = 14;
double number3 = 12.12, number4 = 14.14;
char ch1 = 'a', ch2 = 'b';
//交换前打印各个数据,和交换后的数据进行对比
std::cout << "number1=" << number1 << ' ' << "number2=" << number2 << std::endl;
std::cout << "number3=" << number3 << ' ' << "number4=" << number4 << std::endl;
std::cout << "ch1=" << number1 << ' ' << "ch2=" << ch2 << std::endl;
std::cout << std::endl;
//通过调用上面的三个重载函数交换我们的数据
swap(number1, number2);
swap(number3, number4);
swap(ch1, ch2);
std::cout << "number1=" << number1 << ' ' << "number2=" << number2 << std::endl;
std::cout << "number3=" << number3 << ' ' << "number4=" << number4 << std::endl;
std::cout << "ch1=" << number1 << ' ' << "ch2=" << ch2 << std::endl;
return 0;
}
就像是我们上面代码中所展示的那样,我们定义了很多交换函数的重载函数,这样可以让我们仅仅调用一个函数就可以交换很多数据类型。但是我们会发现一个弊端,那就是我们虽然在调用的时候减少了麻烦,但是在定义的时候依旧是很麻烦。像是我们上面的代码所示的那样, 每一个函数的大致框架都是相同的,仅仅改变了很少的一部分,所以我们在此时就会想到能不能有一种方式,就是定义出一个大致的模板,我们只需要定义依次这个模板就可以重复使用了呢?于是我们C++当中的模板也就产生了。我们可以将上面的代码进行改写:
#include<iostream>
template<typename T>
void swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int number1 = 12, number2 = 14;
double number3 = 12.12, number4 = 14.14;
char ch1 = 'a', ch2 = 'b';
//交换前打印各个数据,和交换后的数据进行对比
std::cout << "number1=" << number1 << ' ' << "number2=" << number2 << std::endl;
std::cout << "number3=" << number3 << ' ' << "number4=" << number4 << std::endl;
std::cout << "ch1=" << number1 << ' ' << "ch2=" << ch2 << std::endl;
std::cout << std::endl;
//通过调用上面的三个重载函数交换我们的数据
swap(number1, number2);
swap(number3, number4);
swap(ch1, ch2);
std::cout << "number1=" << number1 << ' ' << "number2=" << number2 << std::endl;
std::cout << "number3=" << number3 << ' ' << "number4=" << number4 << std::endl;
std::cout << "ch1=" << number1 << ' ' << "ch2=" << ch2 << std::endl;
return 0;
}
我们会发现使用模板我们可以将上面所有大致相同的代码都概括成为同一段代码进行使用,也可以产生同样的效果。代码运行的效果如下:
是不是很神奇?既然知道了使用的方法之后就可以来学习模板的使用了,在C++当中模板的定义大致分为两类:普通模板的使用和类模板的使用。接下来我们就来一步一步学习。
二:普通模板的定义
普通模板的定义其实就是像我们上面的代码所展示的那样,主要是针对一段相似的代码进行模板化的。通常是针对一个函数的定义。通过代码进行一点一点的分析:
我们会发现第49行的代码是一段很陌生的代码,除了这一行下面的代码还是很好理解的。就像是我们正常所说的那样,使用一个统一的不特意制定的类型进行替代,也就是我们代码当中的T。我们通过这个T来交换各种类型的数据。话题回到我们的第49行代码。其实这就是定义模板的一种特定的格式。template表示下面的对象是一个模板。后面的 < > 里面是我们想要替换的数据的类型。当然,我们其中的参数并不是只能有一个还可以有多个,但是都需要由一个 typename 引出(也可以是class,class等价于typename)举一个简单的例子:
#include<iostream>
//定义一个拥有两个参数的模板
template<typename T1,typename T2>
void test(T1 data1, T2 data2)
{
std::cout << "第一类数据:" << '(' << typeid(data1).name() << ") " << data1 << std::endl;
std::cout << "第二类数据:" << '(' << typeid(data2).name() << ") " << data2 << std::endl;
}
int main()
{
int data1 = 12;
char ch = 'a';
double data2 = 12.12;
int data3 = 99;
test(data1, ch);
test(data2, data3);
return 0;
}
我们可以使用多个参数实现对应的数据模板化,这样可以让我们的代码变得更加的灵活。那么普通模板的定义学习完毕之后我们再来认识以下类模板的定义。
三:类模板的定义
对于类模板的定义其实方式和我们的普通模板的定义很相似,但是也有些许的不同。我们通过一段代码来进行学习:
#include<iostream>
//定义一个类模板
template<class T1,class T2,class T3>
class test
{
private:
T1 data1;
T2 data2;
T3 data3;
public:
void print()
{
std::cout << typeid(data1).name() << std::endl;
std::cout << typeid(data2).name() << std::endl;
std::cout << typeid(data3).name() << std::endl;
}
};
int main()
{
test<int,double,char> t1;
t1.print();
return 0;
}
我们可以发现依旧是使用 template 加上 < > 表示下面的内容是一个模板。在表示的时候如果是一个类模板那么在定义变量的时候最好使用class。之后我们就可以书写类,我们只需要将类当中指定的内容替换成为我们在前面声明好的变量名即可。
但是需要我们注意的是:在使用类模板的时候不能仅仅使用类名进行对象的创建,还应该跟上我们想要赋予的变量类型,这样才可以正常的使用,在后面的模板的实例化我们会有更加详细的总结。 代码运行的结果如下:
四:模板的实例化
针对于模板的实例化我们想要讲解的是:模板实例化所可能产生的各种情况。
1.当普通模板定义存在可修改返回值产生的分歧
作为函数模板我们有时候肯定也会需要将返回值设置成可以随意改变的形式,但是有时候我们又会产生一些分歧,例如:假如我们想要定义一个模板既可以同类型的相加又可以不同类型的数据相加呢?(实例如下)
这个时候就会产生报错,想要不修改我们的模板类型,又想让我们的程序正常运行的话,就需要使用一点点小小的技巧。
1.将我们不需要的数据强制类型转换成为另一种数据类型,将两种数据的类型统一。例如:
2.或者使用一种更高级的方式,例如直接向我们的模板指出我们想要的返回值的类型:
这两种方式都可以很好的解决我们上面所遇到的问题。除了普通模板的实例化会出现一些些问题之外我们在实例化类模板的时候还会遇到一些问题。
2:类模板实例化声明所需要的注意事项
其实说到类模板的实例化,我们可以将其理解为是一个类名想象成为一个没有电的电动车。电动车动起来的核心其实是电,电是必不可少的。所以我们在实例化类模板的时候也是一样。类名是我们的电动车,后面赋予的变量的类型是电。在使用函数模板实例化的时候需要将两者结合起来才能作为一个完整的存在进行使用。这也就是我们上面代码:test<int,double,char> t1; 的意义。test<int,double,char> 表示类的类型,t1表示实例化后的对象名。之后对于实例化所产生的对象的使用和正常相同。
Tips:声明类成员函数模板的书写形式
我们在平时书写代码的时候会有一种需求:那就是需要在类中展示的仅仅是声明,而定义需要在另一部分进行。通常情况下的代码的书写的情况是这样的:
#include<iostream>
//定义一个类,在类中只声明类的成员函数,在外部进行定义
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year = 2022, int month = 12, int day = 12)
:_year(year)
, _month(month)
, _day(day)
{
}
void print();
};
void Date::print()
{
std::cout << _year << "年" << _month << "月" << _day << "日" << std::endl;
}
int main()
{
Date d1(2023, 1, 1);
d1.print();
return 0;
}
在定义类的成员函数的时候需要通过类作用限定符进行特定的指定。我们的模板在外部定义的时候其实是一样的也是需要通过域作用限定符进行专门的指定。但是我们需要注意一点:那就是在使用的时候不能仅仅使用一个“电动车”来指定对象,还需要有“电”。代码示例如下:
这个时候我们在定义和实例化类生成对象的时候都需要带上我们指定的变量的类型才可以正常的运行。
那么此上就是我们本次博客的全部内容了,感谢您的观看。