目录
为什么要使用模板?
利用模板机制可以显著减少冗余信息,能大幅度地节约程序代码,进一步提高面向对象程序的可重用性和可维护性。模板是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现代码的重用,使得一段程序可以用于处理多种不同类型的对象,大幅度地提高程序设计的效率。
下面我们展示一段代码来初步了解为什么要使用模板:
#include<iostream>
using namespace std;
#define T char
T add(T x, T y)
{
return x+y;
}
int main(int argc, char *argv[])
{
cout << add(1, 2) << endl;
cout << add(3.1, 2.3) << endl;
cout << add('a', '2') << endl;
return 0;
}
上面这段代码你可以看到我们需要用一个函数来实现不同类型参数的相加,但一般我们使用一个函数是无法做到的,上面我们使用了宏定义,但还是达不到我们想要的功能
模板的基本概念:
C++模板:是支持参数多态化的工具。
实质:设计一种通用类型的函数或类,在函数或类使用时,让数据成员、成员函数、函数返回值可以是任意类型。
目的:让程序员编写与类型无关的代码,是 泛型编程的基础。
函数模板:
针对函数参数类型不同的函数。
类模板:
针对数据成员、成员函数数据类型、参数类型不同的类。
函数模板
所谓函数模板,实际上是建立一个通用函数,其函数返回类型和形参类型不具体指定,用一个虚拟的类型来代表,这个通用函数就称为函数模板。在调用函数时,系统会根据实参的类型(模板实参)来取代模板中的虚拟类型,从而实现不同函数的功能。
函数模板的基本语法:
函数模板语法结构:
template < <类型模板形参列表> > 类型模板形参 函数名(<形参列表>);
注意:
template:模板说明的关键字
<类型模板形参列表>:用 class 或 typename 定义的变量,多个模板形参之间以 空格隔开
class T1: 定义类型模板形参 T1, 用来代替函数的相关数据类型, 简称 模板形参。
template<class T1> T1 add(T1 x, T1 y);
#include<iostream>
using namespace std;
#if 0
template<class T1> T1 add(T1 x, T1 y)//声明定义函数模板
{
return x+y;
}
#else
template<typename T1> T1 add(T1, T1); //函数模板声明
template<class T1> //函数模板定义
T1 add(T1 x, T1 y)
{
return x+y;
}
#endif
int main(int argc, char *argv[])
{
cout << add(1, 2) << endl; //函数模板的 隐式调用,自动匹配数据类型
cout << add(3.1, 2.3) << endl;
cout << add('a', '2') << endl;
cout << add< >(3, 4) << endl; //函数模板的 显示调用,自动匹配数据类型
cout << add< int >(5, 6) << endl; //显示调用,指定模板形参为 int类型,是将 int 传递给了 T1
cout << add< float >(3.12, 4.1) << endl; //显示调用函数模板,实参被隐式转换
cout << add< int >('b', '2') << endl;
return 0;
}
这里需要注意显式调用(注意加<>)与隐式调用,显示调用可以指定模板形参的类型,隐式系统会自动匹配数据类型!!
例1:一个有关指针的函数模板
设计一个通用函数,函数功能,就是为 指针开辟堆区空间,空间大小由使用函数时决定。
#include<iostream>
#include <assert.h>
using namespace std;
#define max 6
// T :类型模板形参,用来替换 数据类型
// m :非类型模板形参,用来替换函数模板中的常量值,非类型模板形参只能是整型,指针和引用
template<class T, int m = max> T * newPoint()
{
T *p = new T[m];
assert(NULL != p);
return p;
}
int main(int argc, char *argv[])
{
char *p = NULL;
p = newPoint<char>();
for(int i = 0; i < max; i++){
cin >> p[i];
}
for(int i = 0; i < max; i++){
cout << p[i] << " ";
}
cout << endl;
return 0;
}
这里我们又引出一个知识点,那就是类型模板形参和非类型模板形参,注意他们的区别,,一般非类型模板形参我们可以直接附上默认值比较方便。
例2:函数模板的重载
函数模板和其他函数一样也可以进行重载!!但是,应当注意template定义部分的每个类型参数前必须有关键字typename或class
#include <iostream>
using namespace std;
template <class Type>
Type Max(Type x, Type y) {
return x > y ? x : y;
}
template <class Type>
Type Max(Type x, Type y, Type z) {
Type t = x > y ? x : y;
t = t > z ? t : z;
return t;
}
int main() {
cout << "33,66中最大值为 " << Max(33, 66) << endl;
cout << "33,66,44中最大值为 " << Max(33, 66, 44) << endl;
return 0;
}
注意:函数模板与同名的非模板函数可以重载。在这种情况下,调用的顺序是:首先寻找一个参数完全匹配的非模板函数,如果找到了就调用它;若没有找到,则寻找函数模板,将其实例化,产生一个匹配的模板参数,若找到了,就调用它。
类模板
所谓类模板,实际上就是建立一个通用类,其数据成员、成员函数的返回类型和形参类型不具体指定,用一个虚拟的类型来代表。使用类模板定义对象时,系统会根据实参的类型来取代类模板中虚拟类型,从而实现不同类的功能。
注意:如果一个类成为了类模板,那么这个类中所有成员函数 将自动变成 函数模板!!!
下面我们来看一段代码初步认识类模板的使用:
注意成员函数在内部声明,在外部定义的语法:
template <typename 类型参数>
函数类型 类名<类型参数>::成员函数名(形参表)
{
·····
}
#include<iostream>
using namespace std;
template<class T>
class Demo{ //声明定义一个类模板
public:
#if 0//成员函数声明定义都在内部
Demo(T x) : x(x){}
void setValue(T x)
{
this->x = x;
}
T getValue()
{
return x;
}
#else//声明在内部,定义在外部
Demo(T );
void setValue(T);
T getValue();
#endif
private:
T x;
};
template<class B> //注意类模板成员函数在类外部定义的语法结构
Demo<B>::Demo(B x): x(x){}
template<class A>
void Demo<A>::setValue(A x)
{
this->x = x;
}
template<class C>
C Demo<C>::getValue()
{
return x;
}
int main(int argc, char *argv[])
{
Demo<int> obj(23); //类模板定义对象时,必须显示说明使用的类模板<>,并且必须传递数据类型
obj.setValue(45);
cout << obj.getValue() << endl;
return 0;
}
再来看个例子把:
#include<iostream>
using namespace std;
template<class T, int max = 12>
class Array{
public:
Array(T len){
for(int i = 0 ; i < max; i++)
arr[i] = len;
}
T getValue(int id);
private:
T arr[max];
};
template<class A, int max>
A Array<A, max>::getValue(int id)
{
return arr[id];
}
int main(int argc, char *argv[])
{
Array<int, 6> obj(12);
cout << obj.getValue(0) << endl;
return 0;
}
在类中我们还学习过一个特殊的函数,友元成员函数,那如果我们在类模板内部和外部定义友元成员函数有什么区别吗,我们来看一段代码:
友元成员函数其实不是类的成员函数他打破了类的封装,所以如果声明定义都是在内部,它不一定是函数模板!!!
#include <iostream>
using namespace std;
template <class T>
class Demo{
public:
Demo(int x):x(x){}
#if 0
friend T operator+(Demo<T> &a,Demo<T> &b)//声明定义在一起都在内部
{
return a.x+b.x;
}
#else
template<class A>//声明在内部,定义在外部
friend A operator+(Demo<A> &,Demo<A> &);//注意声明格式,必须声明为函数模板,不能再定义成T,已经被定义过了
#endif
private:
T x;
};
31 #if 0
32 #else
33 template <class T>
34 T operator+(Demo<T> &obj1,Demo<T> &obj2)
35 {
36 return obj1.x +obj2.x;
37 }
38
39 #endif
40 int main()
41 {
42 Demo<int> obj1(2),obj2(3);
43 cout<<obj1+obj2<<endl;
44 return 0;
45 }
45,1 底端
注意:
类模板的友元成员函数在类外部定义时:
1.友元成员函数的定义和声明都必须使用template关键字单独说明
2.如果一个类是一个模板,那么所有函数都是函数模板吗?不一定,如果友元函数在外部定义那么是,声明定义在内部那该友元函数不一定 是函数模板
3.思考一下如果一个类的友元成员函数是函数模板,那么类一定是一个类模板吗?不一定,因为友元成员函数其实是不属于这个类的成员函数,他打破了类的封装,但如果是运算符重载函数是友元函数并且是模板,那么这个类一定是模板。