模板函数—使用泛型来定义函数
//example: template<typename T> void Swap(T &a,T &b) // 声明或定义 { T temp; temp = a; a = b; b = temp; } int main() { int a = 12; int b = 13; Swap(a,b); cout<<a<<" "<< b<<endl; Swap<int>(a,b); cout<<a<<" "<< b<<endl; }
第一行指出建立一个模板类,并将类型命名为T
,关键字template
和typename
是必须的,除非使用关键字class
代替typename
(两个关键字是等价的)
模板函数使用: ------必须确定T的数据类型,才可以使用。
1自动类型推导: Swap(&a,&b);
-----推导出必须·一致的数据类型
2显示指定类型:Swap<int>(&a,&b);
普通函数和函数模板的区别:
1普通函数调用可以发生隐式类型转换
2对于函数模板,在自动类型推导下,不可以发生隐式类型转换,但在显示指定类型下可以发生隐式类型转换。
普通函数和模板函数调用规则:
1如果函数模板和普通函数都可以调用,优先调用普通函数
2可以通过空模板参数列表,强制调用函数模板 Swap<>(a,b);
3函数模板可以发生重载
4如果函数模板可以产生更好的匹配,优先调用函数模板
总结:在实际中,提供了模板,就不要在提供普通函数。
模板不是万能的,对于特殊数据类型,需要具体化相应的数据类型的实现。
函数模板显示实例化: template<> void Swap(job &a,job &b); job是类类型,就需要实例化实现 —感觉没必要。
函数模板具体化:template <> Swap(char &a,char &b);
/、上述两种只能择其一
//多参数模板 template<typename T1,class T2> void Print(T1 &a,T2 &b) // 声明或定义 { cout<<a<<" "<<b<<endl; } int main() { int a = 12; double b = 13; Print(a,b); Print<int,double>(a,b); }
struct Node{ int x; int y; }; template<typename T> void Print(T &a) // 声明或定义 { cout<<a<<" "<<endl; } template<> void Print<Node>(Node &n){//显示具体化 cout<<n.x<<" "<<n.y<<endl; } int main() { Node node; node.x =12; node.y = 2; Print<Node>(node); int a = 12; Print<int>(a); }
类模板:
如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类类。
1基础类模板定义:
//template <class nametype, class agetype>//多个时 template <class Type> class className{ private: Type items[1]; int top; ......; public: className(); bool isempty(); ..........; }; template <class Type> className<Type>::className(){...} template <class Type> bool className<Type>::isempty(){...}
上面是类实现,类模板一般将实现和声明放一起。且每一个函数前都要使用template
。
如果一定要类外但同一个文件中实现则需要1声明模板,2声明作用域,3且声明作用域时:classname<Type>
如Person类类外实现构造函数: template<class T> Person<T>::Person(T name){...};
如果分文件编写,则在需要该类时,包含头文件为“xxxx.cpp
”直接包含源文件,在源文件中,实现成员函数,同上
对类模板而言,并没有自动类型推导;类模板在模板参数列表可以有默认参数 ----- template <class Type = int>;
则,使用时:class p(2)不会报错;
对类模板而言,成员函数是在调用时创建的,而普通类中的成员函数是一开始创建的。
模板类的继承
在模板类的继承中,需要注意以下几点:
• 如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化
• 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间
class ChildOne:public Parent<int>{ .... };
• 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类
template <typename T> class ChildTwo:public Parent<T>{ .... };
3类模板与友元
全局函数类内声明+类外实例化—直接在类内声明友元即可;在类中:friend void print(Person<T> p){....};
----重要
template<class T> class Node { private: T x; T y; static int all; public: Node(T a,T b); friend void print();//print是所有模板的友元函数,由于没有对象,故只能访问静态变量 friend void print1(Node<T> &);//这里的形参是Node<T>,而不能是Node,因为不存在Node这种类型,只能模板具体化 }; template<class T> int Node<T>::all = 0; template<class T> Node<T>::Node(T a,T b){ all++; x=a; y=b; } void print(){ cout<<Node<int>::all<<endl; } void print1(Node<int> &n)//模板具体化 { cout<<n.all<<" "<<n.x<<" "<<n.y<<endl; } void print1(Node<double> &n)//模板具体化 { cout<<n.all<<" "<<n.x<<" "<<n.y<<endl; } int main() { Node<int> n1(1,1); print();//1 print1(n1);//1 1 1 Node<int> n2(2,2); print();//2 print1(n2);//2 2 2 Node<double> n3(1.2,1.2); print();//1 print1(n3);//1 1.2 1.2 }
全局函数类内声明+类外模板化实现1
template<class T> void print(); template<class T> void print1(T &); template<class T> class Node { private: T x; T y; static int all; public: Node(T a,T b); friend void print<T>();//print是所有模板的友元函数,由于没有对象,故只能访问静态变量 friend void print1<>(Node<T> &); }; template<class T> int Node<T>::all = 0; template<class T> Node<T>::Node(T a,T b){ all++; x=a; y=b; } template<class T> void print(){ cout<<Node<int>::all<<endl; } template<class T> void print1(T &n) { cout<<n.all<<" "<<n.x<<" "<<n.y<<endl; } int main() { Node<int> n1(1,1); print<int>(); print1(n1); Node<int> n2(2,2); print<int>();; print1(n2); Node<double> n3(1.2,1.2); print<int>(); print1(n3); }
全局函数类内声明+类外模板化实现方法2—需要提前让编译器知道全局函数的存在; ---------尽量别用了
1 template class Person;,在最上方
2类外定义 template void printPerson(Person p){…} ,在1下面,在3上方
3在类中声明:friend void printPerson<>(Person p),
类外定义 template void printPerson(Person p){…}