C++模板详解
来源:互联网 发布:java 调用打印机 编辑:程序博客网 时间:2024/06/05 11:07
简介
(1)模板是允许以通用类型的方式来编写程序,其中的通用类型可以是int,double等具体类型。通俗的说使用模板就可以让程序猿编写与类型无关的代码。这种编程方式有时被称为通用编程,而由于类型是参数表示的,因此,模板特性有时也被称为参数化类型。
(2)模板通常有两种形式,一种是函数模板,一种是类模板。
(3)模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
函数模板
模板格式
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表) { 函数体 }
以swap函数为例:
template<class T>void swap(T& a,T& b){ T temp; temp = a; a = b; b = temp;}
要建立一个模板,
①关键字template和class是必须的。当然可以使用typename替换class。
②必须使用尖括号。
③类型名T可以任意选择,只要符合C++的命名规则。
模板使用
函数模板的使用很简单,例如对于swap函数的使用,swap(3,5); 此时编译器就会生成如下代码:
void swap(int& a,int& b){ int temp; temp = a; a = b; b = temp;}
注意:①函数模板不能缩减可执行程序。使用模板的好处是他是生成多个函数的定义更简单。更可靠。
②上面swap如果出现swap(1,2.1)则会则会出现错误。通过声明我们知道swap函数的两个类型应该是一致的。
模板重载
我们可以像重载常规函数那样来重载模板函数。
声明:
template<class T>void swap(T& a,T& b);template<class T>void swap(T* a,T* b,int n);
定义:
template<class T>void swap(T& a,T& b){ int temp; temp = a; a = b; b = temp;}template<class T>void swap(T* a,T* b,int n){ T temp; for(int i = 0;i < n;i++) { temp = a[i]; a[i] = b[i]; b[i] = temp; }}
注意:①并不是所有的模板参数都必须是模板参数类型,也可以是具体类型。具体类型形参在模板定义的内部是常量值,也就是说具体类型形参在模板的内部是常量。 非类型模板的形参只能是整型,枚举,指针和引用,像int,double, String 这样的类型是不允许的。但是int&,int*,对象的引用或指针是正确的.调用具体类型模板形参的实参必须是一个常量表达式,即它必须能在编译时计算出结果。全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。另外,sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。另外。模板代码不能修改参数的值,也不能使用参数的地址。
②当模板函数和常规函数都存在时,并且在调用是函数名和参数都符合,那么优先调用的是常规函数。
显示具体化
在使用swap函数时,我们会遇到交换两个对象或者结构体的值,也会遇到交换两个对象或者结构体中某些变量的值,也就是说并不是交换所有变量的值。这种情况下,模板可能就不够用,所以我们可以通过提供一个具体化函数定义来实现需要的功能。这种方式就是显示具体化。
(1)对于给定的函数名,可以有模板函数,常规函数和显示具体化函数以及重载函数。
(2)显示具体化的原型和定义应以template<>开头,并明确指定类型。
(3)具体化将覆盖常规模板,而常规函数将覆盖具体化和常规模板。
以下是三种形式的swap函数:
常规函数:
void swap(int&,int&);
模板函数:
template<class T>void swap(T& a,T& b);
显示具体化:
template<>void swap<int>(int&,int&)
(4)具体化与实例化
①编译器通过模板为特定类型的生成函数定义时,得到的是模板实例。模板并非函数定义,但使用具体类型的模板实例是函数定义。这是实例化方式被称为隐式实例化。想对应的,显示实例化的句法如下:
template void swap<int>(int,int);
上面语句的含义是使用swap模板生成一哥使用int类型的实例,也就是’使用swap模板生成int类型的函数定义’。
②与显示实例化不同,显示具体化的句法如下:
template<> void swap<int>(int&,int&);template<> void swap(int&,int&);
上面的两个声明等价,显示具体化与显示实例化的区别在于,显示具体化它的含义是‘不要使用模板来生成函数定义,而应使用独立的,专门的函数定义显示的为特定类型生成函数定义’。显示具体化,必须有自己的函数定义。显示具体化声明在template后有<>,而显示实例化没有。
③隐式实例化,显示实例化和显示具体化统称为具体化,他们的相同处在于。它们的表示都是使用具体类型的函数定义,而不是通用描述。
调用哪个函数
对于函数重载,函数模板和函数模板重载,我们在调用函数时,编译器到底调用的是哪个函数??
编译器的这个过程被称为重载解析。具体流程如下:
(1)创建候选函数列表,函数列表包含于被调用函数的名称相同的函数或者模板函数。
(2)利用候选函数列表创建可行函数列表,这些都是函数参数数目正确的函数。这里有个隐式的转化序列,其中包括实参类型与相应的形参类型完全匹配的情况。例如使用float实参可以将float转换为double,从而与double类型形参匹配,就可以使用double类型的模板为float类型生成一个实例。
(3)确定是否有最佳的可行函数。没有则函数调用错误。
参数匹配最佳到最差的顺序如下:
①完全匹配,常规函数优于模板函数。
②提升转换(char,short->int,float->double)
③标准转换(int->char,long->double)
④用户定义的转换,如类声明中定义的转换。
在满足完全匹配时,编译器就能找到最佳的调用函数,除非找到多个函数完全匹配。但有两种情况下,编译器仍能完成重载解析。
①指向非const数据的指针和引用优先于非const指针和引用参数匹配。不过,const于非const的区别只适用于指针和引用指向的数据。
②一种是模板函数,另一种是非模板函数。这种情况下,非模板函数优于模板函数。(包括显示具体化)
如果两个完全匹配的函数是两个模板函数,则较具体的模板函数优先,也就是说编译器推断使用哪种类型时,执行的转换最少。
类模板
模板格式
template<class 形参名,class 形参名,…> class 类名{...};
示例如下:
template<class T>class stack{private: enum{MAX=10}; T items[MAX]; int top; int stacksize;public: stack(); bool isempty(); bool isfull(); bool push(const T& item); bool pop(T & item); stack& operator=(const stack& st);}template<class T>stack<T>::stack(){ top = 0;}template<class T>bool stack<T>::isempty(){ return top == 0;}template<class T>bool stack<T>::isfull(){ return top == MAX;}template<class T>bool stack<T>::push(const T& item){ if(top < MAX) { items[top++] = item; return true; } else return false;}template<class T>bool stack<T>::pop(const T& item){ if(top > 0) { item = items[--top]; return true; } else return false;}template<class T>stack<T>& stack<T>::operator=(const stack<T>& st){ if(this == &st) return *this; delete [] items; stacksize = st.stacksize; top = st.top; items = new T[stacksize]; for(int i = 0;i < top;i++) items[i] = st.items[i]; return *this;}
模板使用
使用方式如下:
stack<int> test1;stack<string> test2;
特性
(1)可以为类型提供默认值
template<class T1,class T2=int> class test{...};
虽然可以为类模板提供默认类型,但是却不可以为函数模板提供默认类型,但是却可以为二者的非参数类型提供默认值。
(2)递归调用模板
stack< stack<int> > test;
(3)模板可以使用多个类型参数
template<class T1,class T2>class test{...};
模板的具体化
(1)隐式实例化
声明了一个或多个对象,指出所需类型,编译器通过模板生成具体类型的类声明。
(2)显式实例化
当使用template关键字并指出所需类型来声明类时,编译器将生成类声明的显式实例化,实例如下:
template class stack<int>;
这种情况下,虽然没有创建或者提及类对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板生成具体化。
(3)显示具体化
显示具体化是特性类型(用于替换模板中的通用类型)的定义,有时候,可能需要在为特殊类型实例化时,对模板进行修改,使其行为不同,在这种情况下,可以创建显示实例化。
显示具体化模板格式:
template<> class classname<>{...};
(4)部分具体化
template<class T> class<T,int>{...};
如果有多个模板可供选择,编译器将会选择具体化程度最高的模板。
除了上面的方式,也可以使用指针对模板进行部分具体化。
template<class T>class test{...};template<class T*>class test{...};
如果提供的类型不是指针将会使用上面的模板,如果使用的是指针,将会使用下面的模板。
成员模板
C++中模板可用做结构,类或者模板类的成员。
template <class T>class test{private: template<class T1> class test_1 { private: T1 val; public: test_1(T1 v = 0):val(v){} void show() const {cout<<val<<endl;} T1 Value() const {return val;} }; test_1<T> q; test_1<int> n;public: test(T t,int i):q(t),n(i){} template<class U> U test2(U u,T t){return ...} void show() const {q.show();n.show();}};
将模板用作参数
template<template <typenameT> class Thing>class crab{private: Thing<int> s1; Thing<double> s2;public: Crab(){}; bool push(int a,double x){return s1.push(a)&&s2.push(b);} bool pop(int& a,double& x){return s1.pop(a)&&s2.pop(x);}}int main(){ Crab<stack> test; test.push(4,3.5); test.pop(4,3.5); return 0;}
模板类与友元
模板的友元分三类:
1.非模板友元
2.约束模板友元,即友元的类型取决于类被实例化的类型。
3.非约束模板友元,即友元的所有具体化都是类的每一个具体化的友元。
模板类的非模板友元函数
template<class T>class HasFriend{ friend void counts();};
上面的声明使counts函数成为模板所有实例化的友元。
如果友元函数的参数是模板本身,该怎么定义??
template<class T>class HasFriend{ friend void counts(HasFriend<T> &);}
上面的声明含义,带HasFriend参数的counts()将成为HasFriend类的友元,同样,带HasFriend参数的counts()函数将是counts的一个重载版本,也是HasFriend类的友元。
模板类的约束模板友元函数
为约束模板友元做准备,要是类的每一个具体化都获得与友元匹配的具体化。具体步骤为三步。
(1)首先,在类定义的前面声明每个模板函数
template void counts();
template void report(T &);
(2)然后在函数中再次将模板声明为友元,这些语句根据类模板的参数的类型声明具体化:
template<class TT>class HasFriend{ friend void counts<TT>(); friend void report<>(HasFriend<TT>&);};
(3)为友元提供模板定义。
模板类的非约束模板友元函数
约束模板友元函数是在类外面声明的模板的具体化。int类具体化获得int函数的具体化,以此类推,通过在类内部声明模板,可以创建非约束友元函数,即每个函数具体化都是每个类的具体化友元。
template<class T>class HasFriend{ template<class C,class D> friend void show(C&,D&);};
- JSTL 模板中 <c:forEach> 标签详解
- [模板] + [详解]
- [模板] + [详解]
- C++-模板
- C++-模板
- C++:模板
- C++:模板
- C++:模板
- 模板 (C++)
- C ++ 模板
- C ++ 模板
- 【C++】模板
- c++----------模板
- C++--------------------------------------------模板
- c++--模板
- C++:模板
- C/C++学习笔记(2)--函数模板template<typename T>,类模板详解(1)
- 【基础C&C++】模板
- FinsTCP协议的Java Socket
- 合并两个已排序的链表
- File size exceeds configured limit (2560000), code insight features not available
- 设计模式 之 准备开始之前
- 一个农村大男孩的IT梦
- C++模板详解
- 32 流程控制
- 浅谈PHP值传递与值引用
- SAPテーブル一覧
- 在线培训-虚拟演播室嵌入网页直播
- 2017 年 PHP 程序员未来路在何方
- Ubuntu16.04下安装编译Caffe
- 银行电商平台技术解决方案
- JavaScript开发的45个经典技巧