C++ 函数,类,内联模板

本文详细介绍了C++中的泛型编程,涵盖函数模板和类模板的定义、使用、参数列表中的默认参数、成员函数创建时机、模板类的继承等问题。通过实例展示了如何使用函数模板实现不同类型的变量交换,以及类模板的创建和调用,强调了内联模板在类模板中的重要性。此外,还讨论了类模板的分文件编写问题和实例化问题。
摘要由CSDN通过智能技术生成

本文涉及:

泛型编程的解释
泛型模板的解释 泛型模板两种形式的解释
定义一个泛型模板

函数模板部分:
函数模板的解释 定义一个函数模板
函数模板的实际应用 函数重载和函数模板的异同
使用函数模板实现各类变量交换功能
两种函数模板调用方式的解释
定义有多个泛型参数函数模板 多参数的函数模板注意事项
函数模板注意事项及总结

类模板部分:
类模板必要性的解释 声明和定义类模板的格式
类模板和函数模板的异同 建立一个典型的类模板
在类模板外部定义成员函数(非内联函数) 定义类模板构造函数和普通成员函数
类模板编写一个与栈机制相似的函数(非内联函数)
类模板使用多种泛型
类模板的调用(调用类模板时显式指定类模板使用的泛型类型)
泛型模板的参数列表中的默认参数)
泛型模板的参数列表中有默认参数时 类模板进行实例化类对象时的几种情况的说明
类模板中成员函数的创建时机的说明
类模板实例化出的对象作为参数传递的说明
类模板和友元函数 模板类和模板函数的声明和联系的说明
类模板的类继承问题
模板类的继承方式的说明
类模板的分文件编写问题
类模板实例化的问题
类内联模板的解释
类模板注意事项及总结


前言:

此前我们已经了解了两种C++程序设计范型:
1.面向过程式的范型(把程序划分成不同的函数).
2.面向对象式的范型(把代码和数据组织成各种各样的类,并建立类之间的继承关系).
C++程序设计范型除了上述两种,还有一种名为泛型编程的范型.

泛型编程技术支持程序员创建函数和类的蓝图(即模板template),而不是创建具体的函数和类,即以一种独立于任何特定类型的方式编写代码(不考虑具体数据类型的编程方式)

使用泛型编程技术的目的是只用把算法实现一遍,就能适用于多种数据类型.其优势在于能够减少重复代码的编写.


泛型模板

模板是创建泛型类或函数的蓝图或公式,例如库容器,迭代器等算法,都是泛型编程的例子,它们都使用了模板的概念,拿库容器来说,每个容器都有一个单一的定义,例如向量,我们可以定义许多不同类型的向量,例如vector<int>vector<string>等等…

模板可以没有任何类型,所以可以使用这个模板处理任何类型的数据,所以模板可以作为对类型进行参数化的工具

当程序需要用到这些函数中的某一个时,编译器将根据模板即时生成一个能够对特定数据类型进行处理的代码版本.

泛型编程技术能让程序员用一个解决方案解决多个问题

模板通常有两种形式:

函数模板:针对仅参数类型不同的函数

类模板:针对仅数据成员和成员函数类型不同的类

如何编写和使用自定义的泛型模板:

在泛型编程技术里,我们仍需要编写自己的函数和类,但不必限定它们所使用的数据类型,只需要使用一个占位符(通常用字母T表示),然后用这个占位符来编写函数.

程序需要这段代码时,此时需要提供数据类型,编译器将根据建立的模板即时生成实用的代码.
简单来说,编译器把模板里的每一个T替换为此时提供的数据类型(类似于#define).

泛型模板的定义的一般形式如下所示:

template <classtypename T,class T2,class T3...>

template关键字: 用于声明开始进行泛型编程
class关键字: 用于 声明泛指类型(上述代码中泛指类型为T)
T1占位符:函数所使用的数据类型的占位符名称,这个名称可以在函数定义中使用

其中template为声明泛型编程的关键字,其后的尖括号内有多个class T,用来告诉编译器:字母T将在接下来的函数里代表一种不确定的数据类型.
在告诉T是一种类型之后,就可以将T像对待一种普通数据类型那样的使用它了.

注意:此处的关键字class并不指T为一个类,class只是一种约定俗成的写法,且这个class在声明模板的过程中必不可少.

除了使用class作为不定数据类型T的关键字外,我们话可以使用typename作为其关键字,例如:<typename T>,其含义和效果与class是一样的.

泛型模板的声明或定义只能在全局,命名空间类范围内进行(即不能在局部范围和某函数内进行声明,比如不能在main函数中声明或定义一个泛型模板)


函数模板

函数模板是一种特殊的函数,可以使用不同的类型对其进行调用,对于功能相同但传入参数类型不同的函数,不需要重复编写代码,函数模板和普通函数看起来很相似,区别就是模板函数的参数类型可以被参数化.

声明一个函数模板:

template <class 不定类型模板形参名,class 不定类型模板形参名2>
返回类型 函数名(参数列表)
{
   
	函数体...
}; 🔥//声明一个函数模板后,此函数模板便具有了全局作用域

定义一个函数模板:

template <class 不定类型模板形参名,class 不定类型模板形参名2>
返回类型 函数名(参数列表)
{
   
	函数体...
}

🌟其中templateclass泛型模板关键字,<>括号中的参数称为模板形参(也可称为泛指类型),模板形参和函数形参相似,且模板形参不能为空类型.

一旦声明了模板函数就可以用模板函数中的形参名声明函数和类中成员变量和成员函数(即可以在该函数中使用内置类型的地方都可以使用模板形参名作为类型名)

模板形参需要使用该模板函数所提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型,就称其实例化了函数模板的一个实例.

函数模板的实际应用:

从以前的程序中可以知晓,交换两个变量的值是一种几乎所有程序都需要用到的基本操作.因为这种交换如此常见,所以把它编写为一个函数模板是个好主意.

一个以引用做传入参数的交换变量函数(非函数模板):

void swap(int& a, int& b)
{
   
	int temp = a;
	a = b;
	b = temp;
}

上述程序中,若是现在只是需要调换两个类型为int的变量,确实可以使用,但是如果我们此时想再次利用这个函数完成两个double类型变量的交换,该怎么办?

方案一(函数重载): 可以再增加一个swap(double &a,double &b)函数来适配double类型的转换,因为C++支持函数重载,但是如果有非常多的类型需要去适配,代码将会变得十分的冗杂.所以我们需要函数模板,能根据特定的变化对输入参数进行适配.

方案二(函数模板): 函数模板较于函数重载的优势在于,不用为每一种数据类型分别编写一个函数,只要告诉编译器你已经为此准备好了一个模板就行吗,建立好函数模板后再使用swap()函数,编译器将根据模板自动创建一个函数,该函数会自动使用正确的数据类型来完成交换变量值的任务.

使用函数模板实现各类变量交换功能:

template <class T> //🔥首先声明一个类型不定的模板 T
void Swap(T& a, T& b) //🔥声明并定义一个函数模板(传入参数为模板变量T)
{
   
	T temp = a;
	a = b;
	b = temp;
}

注意:💣函数模板不能被分为原型(声明)和实现(定义)两部分,函数模板必须同时进行声明和定义!

int main()
{
   
	//🔥整型量的交换
	int i1 = 5;
	int i2 = 8;
	cout << "模板函数交换前, i1= " << i1 << "i2= " << i2 << endl;
	Swap(i1, i2);
	cout << "模板函数交换后, i1= " << i1 << "i2= " << i2 << endl;
	
	//🔥字符型量的交换
	string s1 = "德顿";
	string s2 = "杰克";
	cout << "模板函数交换前, s1= " << s1 << "s2= " << s2 << endl;
	Swap(s1, s2);
	cout << "模板函数交换后, s1= " << s1 << "s2= " << s2 << endl;
	return;
}

运行结果:
在这里插入图片描述
可以看到,我们通过使用函数模板,根据具体类型的参数化,就能适用于不同类型的变量交换,达到了代码复用的效果.

实现了各类变量交换的函数模板Swap的形式为

template <class T>
void Swap(T& a, T& b){
   }

当调用这个Swap模板函数时类型T就会被调用时的类型所代替,比如swap(i1,i2)其中i1i2int型,这时模板函数Swap中的形参T就会被int所代替,此时函数模板便变成void Swap(int & a, int& b){}…以此类推,其他类的变量通过调用模板函数时传入参数类型的不同,也会根据不同的类型调整函数模板中的不定类型T,这样便是实现了多类型通用一个函数的功能.

使用函数模板时有两种调用方式:

自动类型推到调用Swap(i1,i2):

int i1 = 5;
int i2 = 8;
swap(i1,i2)

具体类型显式调用Swap<int>(i1,i2):

以上述程序为例,为了明确的表明swap()是某类型的函数模板,我们还可以使用以下语法来调用这个函数,即:

int i1 = 5;
int i2 = 8;
swap<int>(i1,i2)

这将明确的告诉编译器此函数应该使用哪一种类型(不加也不影响程序)

定义有多个泛型参数函数模板 :

一个作为模板使用的函数往往同时需要处理多种不同类型的参数,由于C++并没有限制只能使用一个类型的占位符,如果函数模板中需要多种类型,只需要先在泛型模板定义多个模板形参,再根据具体情况在模板函数中多使用几个模板形参作为占位符即可,例如:

template <class T1,class T2> //🔥建立模板时 设置两个不同模板形参T1 和 T2
void swap(T1& a, T2& b) //🔥由于模板形参不同,所以此时传入的两个形参也可以是不同类型·
{
   
	cout << a << "   " << b << endl;
}

int main()
{
   

	int a = 5;
	string b = "ABC";
	swap<int,string>(a,b);
	//运行结果:  "5  ABC"
}

多参数的函数模板注意事项:

1.编译器是无法自动推导返回值类型的,例如:

template <class T1,class T2,class T3>
T1 swap(T2& a, T3& b) //🔥函数的返回值类型 形参a类型 形参b的类型 皆为互相独立的泛指类型
{
   
	cout << a << "   " << b << endl;
	return a; 
	//🔥返回类型为泛指类型T2的参数a,此时由于我们不能确定函数返回值的类型,所以设置其返回值为T1,又因
	//T1这个返回值的泛指类型和最终函数返回的泛指类型T2不一致,程序又不会自动推导其返回值,导致程序错误
}

cout << swap(i1,i2) << "\n\n";//💣error!,无法自动推导函数返回值

这个机制导致我们需要通过手动指定模板函数类型参数,才能给模板函数的返回类型单独指定一个泛指类型(否则只能将其返回值类型绑定为其中某个形参的泛指类型).

2.可以从左向右部分指定模板函数类型参数,例如:

template <class T1,class T2,class T3>
T1 swap(T2& a, T3& b) //🔥函数的返回值类型 形参a类型 形参b的类型 皆为互相独立的泛指类型
{
   
	cout << a << "   " << b << endl;
	return a;
}

int i1 = 2;
string i2 = "A";
cout << swap<int,int,string>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值