✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿
🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
🌟🌟 追风赶月莫停留 🌟🌟
🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
🌟🌟 平芜尽处是春山🌟🌟
🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
🍋模版初阶
- 🍑泛型编程
- 🍍泛型编程的定义
- 🍍泛型编程的由来
- 🍍泛型编程的应用
- 🍑函数模版
- 🍍函数模版概念
- 🍍函数模版原理
- 🍍函数模版应用
- 🍍模版参数的匹配原则
- 🍑类模版
- 🍍类模版的概念
- 🍍类模版实例化
🍑泛型编程
🍍泛型编程的定义
在C++中,泛型编程是一种编程范式,它允许程序员编写与数据类型无关的代码。这样,你可以写出一段通用的代码,然后应用于多种数据类型,而不需要为每种数据类型都重新编写代码。泛型编程的核心概念是模板。
C++中有两种主要的模版:函数模版和类模版。
🍍泛型编程的由来
泛型编程的概念起源于对算法和数据结构通用性的追求。其最早的实践可以追溯到1970年代的CLU和Ada语言。在这些早期的语言中,程序员开始尝试编写不依赖于特定数据类型的通用算法。
随着时间的推移,泛型编程的概念逐渐发展并得到了更广泛的应用。1980年代,泛型编程开始被正式提出并应用于一些主流的编程语言中,如C++。C++引入了模板(template)机制,允许程序员编写可以处理多种数据类型的通用代码。
在1990年代,随着面向对象编程(OOP)的普及,泛型编程也开始被应用于面向对象的语言中,如Java。Java在J2SE 5.0(JDK 1.5)中引入了泛型技术,使得类和接口可以使用类型参数,进一步提高了代码的灵活性和重用性。
此后,泛型编程逐渐成为了现代编程语言中的一个重要特性,被广泛应用于数据结构、算法、框架等领域。它不仅提高了代码的重用性和灵活性,还增强了类型安全性,有助于减少运行时错误。
🍍泛型编程的应用
平常我们在解决一个算法时,需要构建一个函数来解决此算法,而这个函数我们需要写出通用的一个写法,不然没有什么意义,比如此函数可以解决参数为int型的,但解决不了类型为double型的,这就要你重新写一个函数可以解决类型为double的,这样就会很麻烦,比如:
int Add(int a, int b)
{
return a+b;
}
float Add(float a, float b)
{
return a+b;
}
double Add(double a, double b)
{
return a+b;
}
上面三个加法函数只能解决自己类型的参数问题,算法相同,仅仅只是参数类型不同,这就很麻烦。
这就要提到我们今天的主角——模版,我们能不能通过给系统一个模版,然后编译器根据不同的类型来生成代码呢?
看下面分析,主要从函数模版和类模版介绍。
🍑函数模版
🍍函数模版概念
函数模板是C++泛型编程的一个重要组成部分,它允许程序员定义一种行为,该行为可以作用于多种不同的数据类型,而无需为每种数据类型都重新编写函数。函数模板通过引入类型参数(通常是使用typename或class关键字声明的),来创建一种可以处理多种数据类型的函数。
函数模板的定义使用template关键字开始,后面紧跟一个尖括号==<>,其中包含了类型参数列表==。类型参数类似于函数参数,但它们是用于指定函数内部使用的数据类型的。
🍍函数模版原理
函数模版的原理是基于数据类型的参数化,关键是将一组算法相同,但所处理数据类型不同的重载函数。
如:
template<class T>
T Add(T a, T b)
{
return a+b;
}
该用template修饰的Add()函数,就是一个加法函数的模版。
在编译器实际应用过程中并不是直接调用这个模版,而是间接通过模版构建一个关于此类型的加法函数,也就是上面那三个函数。
🍍函数模版应用
上图中,我们展示同类型参数传入的模版,如果两个参数是不同类型的该怎么写呢?
不同类型:
int Add(int a, float b)
{
return a+b;
}
float Add(int a, float b)
{
return a+b;
}
double (int a, double b)
{
return a+b;
}
上面代码就是不同类型的参数,但实际返回还是看函数的类型,如第一个加法函数,虽然a是int类型,b是float类型,最后返回的确实int类型,因为此函数是int类型的。
那这个有没有模版呢?答案肯定是有的。
template<class T1, class T2>
T1 Add(T1 a, T2 b)
{
return a+b;
}
上诉代码就是该类型模版的写法。
同样的道理,从图中我们可以看出,当需要用到此时函数时,并不是实际去调用这个函数,而是这个编译器可以根据你提供的这个模版,再根据你参数的类型自动生成这个类型的函数。
总结,关于这个函数模版,在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
当我们知道这个模版的用法后,其实表面的工作由我们转到了编译器,编译器帮我们做了许多的工作。
🍍模版参数的匹配原则
关于这个模版参数的匹配原则,该程序中有该函数模版,但也有该类型的处理函数,那实际调用过程中,编译器会优先使用那个呢?接下来我直接用程序执行结果来给大家演示:
#include<iostream>
using namespace std;
int Add(int a, int b)
{
cout << "没有使用类模版函数函数" << endl;
return a+b;
}
template<class T>
T Add(T a, T b)
{
cout << "使用了类模版函数" << endl;
return a+b;
}
int main()
{
int a = 2;
int b = 8;
int ret = Add(a, b);
cout << ret << endl;
return 0;
}
从上图中就可以看到结果了。
对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模
板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
🍑类模版
🍍类模版的概念
类模板是一个参数化类型,它允许我们创建一个通用的类,其数据成员、成员函数的返回类型和形参类型不是具体指定的,而是用一个虚拟的类型或值来代替。这样,当我们实际使用类模板时,可以根据传入的类型或值参数来生成特定类型的类实例。
类模板的定义通常以关键字template开始,后面跟着模板参数列表,然后是类名。模板参数列表可以包含类型参数和非类型参数。类型参数通常使用typename或class关键字来修饰,表示不确定的类型;非类型参数则是具体的参数,如整数或字符等。
使用类模板有许多优点,包括提高代码的可重用性、效率和可维护性,减少代码冗余,以及提高代码的可读性和性能。通过类模板,我们可以轻松地创建支持各种数据类型的类,而不必为每个数据类型单独编写代码。
🍍类模版实例化
具体格式看下列代码:
template<class T1, class T2, class T3,......>
class //类模板名
{
// 类内成员定义
};
上诉就是类模版的格式。
// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>
中即可,类模板名字不是真正的类,而实例化的结果才是真正的类.
关于类模版后面用起来会非常方便,目前大家可能接触不多,等大家学到后面,就能理解了。
关于本本章知识点如果有不足或者遗漏,欢迎大家指正,谢谢!!!