一.在函数中使用模板:
template <typename形参1,typename形参2> int compare(const形参1&a,const形参2&b);
类格式为:
template <class 形参1> class YuTest
{
public:
形参1 GetNum();
……
}
注意:
1.每个模板形参都必须有关键字typename或class来修饰。不可以漏写。
如: template<typenameT,U> 的写法是错误的。
2.模板形参有两种:①类型化形参,该种形参接收一个类对象,可以用typename或class来修饰;②非类型形参,或者说常量表达式形参,该种形参接收一个常量,可以用typename或class来修饰。
typename和class两个关键字的区别在于,class使用更早,故早期版本主要使用class。
3.每个模板形参个数并非是任意定义的。一旦定义,后面的函数参数中或类的实现中就必须要用到,否则会编译出错。
4.多个模板形参的名称必须不同。如:template <typenameT,typename T>这样的写法是错误的
4.每个template只对其后的一个函数或类起作用。
5.模板类的声明和实现就目前的编译器而言,必须都在h文件里写。无论是模板类型还是模板函数都是如此。若是模板类,则其成员函数实现也都要在.h中实现。
6.模板函数定义与模板类型方式相同。使用的时候当做一个正常的函数直接调用函数名即可,无需其他操作。
template <class T>
int f(T t)
{
return t + 1;
}
然后直接可以:
cout<<f(10)<<endl;
其输出值为11。
7.使用模板类必须显示地为模板形参指定参数。
如上面的YuTest,需要这样调用:YuTest<int>m_yuTest;或YuTest<CString>m_yuTest;
技巧:使用关键字typedef为每一个类型定义一个别名,使用时直接引用别名。
typedef YuPointT<int> YuPoint;
二.在类中使用template,有两种方式:
①类中有多个成员函数及成员变量是template类型,那么该类就称之为模板类,需要在类定义前加template <typename形参1>:
template <class 形参1>
class YuTest
{
public:
形参1 GetNum();
……
}
实现:
template<typename形参1> YuTest <形参1>::GetNum()
{
}
②类中只有1个成员函数是template类型,那么只需要在函数前加template <typename形参1>即可:
class YuTest
{
public:
template <class T>
T GetNum(T p1);
……
}
实现:
template<typename T > YuTest < T >::GetNum()
{
}
三. template 的编译是直到调用时才进行的
编译器在遇到template的实现时并不为其产生机器代码,而是等到template被指定某种类型时才会编译。
因此,以下调用是合法的:
class A
{
public:
A() { x = 1; }
int x;
};
class B
{
public:
B() { x = 2.0; }
double x;
};
class C
{
public:
C() { x = 3.0; }
float x;
};
template <class T>
int f(T t)
{
int a;
k = (int)t.x;//注①特别注意这里,见下文解释
return k;
}
void main()
{
A a;
B b;
C c;
cout << f(a) << endl;
cout << f(b) << endl;
cout << f(c) << endl;
}
如上,一般来说,
template所指定的类型
T可以是任何类型。直接使用该类型是可以的。但调用一个未知类型的成员变量或者成员函数是否也可以?
就如同注①那样,函数f()传入的参数是一个未知类型T,但在函数内却使用了的成员变量x。但T的类型未知,编译器如何知道该类型内是否有成员变量x?
首先,这样使用是合法的。对于上面的程序而言,编译也会通过并顺利运行。
其原因就是编译器在遇到template的实现时并不为其产生机器代码,而是等到template被指定某种类型时才会编译。当在main()中运行cout<<f(a)<<endl;时,就会把a赋给T,此时编译器便会编译一次f()的代码,用a将其中的T全部替换。编译f()中的k = (int)a.x;时,顺利通过。同理,运行cout<<f(b)<<endl;时,编译器会编译第二次f()的代码。整个程序结束,编译器会将f()编译三次。
若将cout<<f(c)<<endl;改为cout<<f(9)<<endl;,当编译器通过了前两次f()的编译,来对该行代码进行编译时,发现其参数9是个int型,而int并无x成员变量,于是在编译k = (int)9.x;时就会出错。
编写程序时,若需要使用以上的写法,无法使用自动补全功能,也就是靠使用”.”或者”->”是无法自动补全成员变量或成员函数的。因此需要手动填写。只要保证函数内所有涉及的操作在传入的参数类中均有实现即可。