-
泛型编程
编写与类型无关的通用代码,是代码复用的一种手段
在实现swap函数的时候,我们可以利用函数重载来实现两个数字之间的互换,代码演示:
#include<iostream> using std::cout; using std::endl; void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } void swap(double& a, double& b) { double tmp = a; a = b; b = tmp; } int main() { int a = 0; int b = 1; swap(a, b); cout << a << endl; cout << b << endl; double c = 0.1; double d = 0.2; swap(c, d); cout << c << endl; cout << d << endl; }
输出结果为:
注意:上述代码虽然利用函数重载避免了取函数名的麻烦,但是重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时,就需要增加对应的函数 ,那么我们可不可以使用泛型编程的手段来解决这类问题?此时就提出了函数模板 -
函数模板
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定 类型版本。
2.1函数模板格式
template <typename T1,typename T2,… typename Tn>
返回值类型 函数名(参数列表)
{}代码演示:
#include<iostream> using std::cout; using std::endl; template<typename T> //也可以写成这样:template<class T> void swap(T& left, T& right) { T tmp = left; left = right; right = tmp; } int main() { int a = 0; int b = 1; swap(a, b); cout << a << endl; cout << b << endl; double c = 0.1; double d = 0.2; swap(c, d); cout << c << endl; cout << d << endl; }
输出结果为:
注意::typename是用来定义模板参数关键字,也可以使用class来代替,但是不能用struct来代替class2.2函数模板原理
函数模板本身并不是一个函数,可以说它是一个拓印工具,我们将它交给编译器,编译器根据实参的数据类型来推测出形参的数据类型,然后再实例化成一个具体的函数
如图所示:
注意: 在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供 调用,比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然 后产生一份专门处理double类型的代码2.4函数模板实例化
2.4.1隐式实例化
让编译器根据实参推演模板参数的实际类型
格式:函数名 <模板参数的实际类型>()
注意:上面的所有代码都是隐式实例化,通过实参来推演模板参数的实际类型
2.4.2显示实例化
当编译器无法通过实参推出模板参数类型,或者根本没有实参的时候,需要用到显示实例化
代码演示:
#include<iostream> using std::cout; using std::endl; template<class T> T Add(const T& left,const T& right) { return left+right; } void founc() { T tmp; cout<<tmp<<endl; //不考虑没定义tmp报的错误 } int main() { Add(1,1.5); //此语句会报错,因为再利用实参推模板参数的时候,由于模板参数的类型只有一个T,所以无法推出两个类型 //为了解决这个问题,我们可以自己强制类型转换,将1的类型int转为double,也可以使用显式类型转换,如下语句: Add<double>(1,1.5); //当没有实参的时候也必须使用显示实例化,如: founc(); //此语句会报错,因为没有实参,模板无法推出T是什么类型,通过显示实例化改为如下语句: founc<int>(); return 0; }
2.5模板参数的匹配原则
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
代码演示:
#include<iostream> using namespace std; //专门处理int相加的函数 int Add(int left,int right) { return left+right; } //模板相加函数 template <class T> T Add(T left,T right) { return left+right; } int main() { Add(1,2);//与专门处理int相加的函数匹配,不需要模板实例化 Add<int>(1,2);//与模板相加函数匹配,需要模板显示实例化 }
- 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
代码演示:
#include<iostream> using namespace std; //专门处理int相加的函数 int Add(int left,int right) { return left+right; } //模板相加函数 template <class T1,class T2> T Add(T1 left,T2 right) { return left+right; } int main() { Add(1,2);//与专门处理int相加的函数匹配,不需要模板实例化 Add(1,2.1);//与模板相加函数匹配,需要模板隐式实例化 }
-
类模板
3.1类模板格式:
template<class T> class 类模板名 { //类内成员定义 }
代码演示:
//以顺序表为例 template<class T> class Seqlist { public: Seqlist()//构造函数 { _arr = new T[10]; _size = 0; _capacity = 0; } ~Seqlist()//析构函数 { delete[] _arr; } Seqlist(const Seqlist<T>& data)//拷贝构造 { _arr = data._arr; _size = data._size; _capacity = data._capacity; } Seqlist<T>& operator=(const Seqlist<T>& data)//赋值运算符重载 { _arr = data._arr; _size = data.size; _capacity = data._capacity; } void PushBack(const T& data)//尾插 { //不考虑增容问题 _arr[_size] = data; _size++; } void PopBack()//尾删 { if (_size > 0) --_size; } void Print()//打印 { for (size_t i = 0; i < _size; ++i) cout << _arr[i]<<" "; cout << endl; } private: T* _arr; size_t _size; size_t _capacity; };
3.2类模板实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
代码演示(利用3.1的顺序表的类模板):
#include<iostream> int main() { Seqlist<int> s1; Seqlist<double>s2; s1.PushBack(1); s1.PushBack(2); s1.PushBack(3); s1.Print(); s2.PushBack(1.1); s2.PushBack(2.2); s2.PushBack(3.3); s2.Print(); }
输出结果为:
C++函数模板初阶
最新推荐文章于 2024-02-23 20:47:38 发布
本文介绍了C++中的函数模板,作为泛型编程的一种方式,它允许编写与类型无关的通用代码。文章详细讲解了函数模板的概念、格式、工作原理、隐式和显示实例化,以及模板参数的匹配原则。此外,还探讨了类模板的格式和实例化过程。
摘要由CSDN通过智能技术生成