C++(15):模板(Template)
2016-12-25 12:22
441 查看
引言
还是那句话。为了更多的兼容性。为了写更少的代码。有了模(mu,二声)板。模板不是类。
模板分函数模板,类模板。以下分别总结。
函数模板
基本写法
template <typename T> void swap1(T & t1, T & t2){ T temp; temp = t1; t1 = t2; t2 = temp; }
int x1 = 1; int x2 = 3; cout<<"x1 = "<<x1<<", x2 = "<<x2<<endl; swap1(x1,x2); cout<<"x1 = "<<x1<<", x2 = "<<x2<<endl;
x1 = 1, x2 = 3
x1 = 3, x2 = 1
因为是引用传递,所以确实交换了值。
注意。
1. 要写关键字template以示这是模板,之后写<模板参数>;
2. 尽量不要写class T,class U,不要写“class”,这样具有误导性。尽管java就是这么写。尽量写“typename”;
3. 之后写函数体。
4. 编译器在编译的时候,会推断出这些T、U究竟是什么类型,进而实例化一个特定版本的函数;
5. 上面可以写swap1(x1,x2),但是不能写swap(1,3)。编译器报错,说不是int型变量…
6. 不能写swap(i,d),其中i是int型,d是double型。因为这个函数模板声明的时候,两个形参都是T,实参在传参数的时候,类型要一致!
第5条的解决方案。函数形参加const,实参就可以是常量了
template <typename T> bool compare(const T & v1,const T & v2) { return (v1<=v2); }
此时,调用这个函数时,实参可以是同一类型的常数,也可以是同一类型的变量。
cout<<compare(2.3,3.4)<<endl; cout<<compare(x1,x2)<<endl;1
0
第6条的解决方案。模板参数列表中,参数写两个不同的。同时函数形参,也写两个不同的,就支持不同类型的变量了。
template <typename T1,typename T2> bool compare(const T1 & v1,const T2 & v2) { return (v1<=v2); }
cout<<compare(x1,0.3)<<endl;
越来越兼容了……。
甚至还可以写成:compare(const T1 & t1, int i)这种样子…
函数模板的重载
Can overload function templates only when each version takes a different argument list– allow compiler to distinguish
template <typename T> T FindMax(T x, T y) { T max = x; if (y > max) max = y; return max; } template <typename T> T FindMax( T x, T y, T z) { T max = x; if (y > max) max = y; else if (z > max) max = z; return max; }
【经典案例】
用模板实现从3个学生中,找出最高分的同学。
连返回的数据类型都可以写成模板!太灵活了。
#include <iostream> using namespace std; // 三个数找大小都不会了 -_-! // 先假设第一个数是最大的,然后跟第二个比,再跟第3个比! // 【严重警告】 最基本的查找、排序还是要会的啊! // 【严重警告】 最基本的三种数据结构必须要会啊! template <typename T> T findMax(T & a, T & b, T & c) { T maxT = a; if (maxT<b) { maxT = b; } if (maxT<c) { maxT = c; } return maxT; } // overloading function findMax template <typename T> T findMax(T & a, T & b) { return (a<b)?b:a; } class CStu { /* friend function */ friend ostream & operator<<(ostream & os, CStu & cStu); // friend bool operator<(CStu & o1, CStu & o2); // overloading operator < public: CStu(double score_para):score(score_para){}; ~CStu(){}; // overloading operator < as a member function bool operator<(CStu & o2); void setScore(double score) { this->score = score; } double getScore() const { return this->score; } private: double score; }; /* friend function 需要在类外面定义 */ ostream & operator<<(ostream & os, CStu & cStu){ os<<cStu.getScore(); return os; } // bool operator<(CStu & o1, CStu & o2) { // return (o1.getScore()<o2.getScore()) ? true : false; // } bool CStu::operator<(CStu & o2) { return (this->getScore()<o2.getScore()) ? true : false; } int main () { CStu cStu1(88.6); CStu cStu2(77.4); CStu cStu3(66.3); CStu cStuMax = findMax(cStu1,cStu2,cStu3); cout<<"最高分:"<<cStuMax<<endl; CStu cStuMax2 = findMax(cStu2,cStu3); cout<<"最高分:"<<cStuMax2<<endl; system("pause"); return 0; }
运行结果。
最高分:88.6
最高分:77.4
几点注意。
1. 3个数,找出最大的。先假设第一个数是最大的,然后跟第二个比,再跟第3个比!
2. 现在形参确实是T & ,是一个类了!所以要对<运算符进行重载!注意啊,"<"也是一个函数!返回值是bool类型的!
3. 重载可以通过成员函数重载,也可以用友元函数进行重载。
Review:运算符重载
显式地指定类型
When calling a template function, the arguments dictate the types to be usedTo override a deduced type:
someFunction<char>(someArgument);
- useful when at least one of the types you need to generate in the function is not an argument
【经典案例】
#include <iostream> #include <string> using namespace std; // 在调用时显示地指定模板的类型 template <class T> T doubleVal(T val) { val *= 2; return val; } template <class T, class U> T tripleVal(U val) { T temp = val * 3; return temp; } template <class T, class U> U tripleVal2(T val) { T temp = val * 3; return temp; } template <class U, class T> T tripleVal3(U val) { T temp = val * 3; return temp; } int main () { int a = 4; double b = 8.8; cout <<a<<" & "<<doubleVal(a)<<"\n"; cout <<b<<" & "<<doubleVal(b)<<"\n"; cout <<b<<" & "<<doubleVal<int>(b)<<"\n"; // 这个<int>的意思是,把T直接指定int型 // 因此,输入的时候,double就直接被转成了int型 cout << tripleVal<int>(a) << "\n"; // 把模板里第一种类型,T,指定为int --> 输出12 cout << tripleVal<int>(b) << "\n"; // 把模板里的第一种类型,T,指定为int --> 8.8*3 = 26.4 --> 转int 输出26 cout << tripleVal<int,double>(b)<< "\n"; // --> 把第一种类型,T,指定为int,把第二种类型,U,指定为double --> 8.8*4=26.4 --> 转int // 输出26 cout << tripleVal<int, int>(b) <<"\n"; // 把 T 和 U 都指定为 int型, --> 8.8变成8, 24 cout << tripleVal2<int,double>(b)<< "\n"; // 第一种类型,还是T! cout << tripleVal2<int, int>(b) <<"\n"; // cout << tripleVal3<int,double>(b)<< "\n"; cout << tripleVal3<int, int>(b) <<"\n"; system("pause"); return 0; }
运行结果。
4 & 8
8.8 & 17.6
8.8 & 16
12
26
26
24
24
24
24
24
注意,模板的显式指定,是从template <typename T1, typename T2, ...> 这里开始,从左往右,显式指定!
类模板
写法
A class template defines a family of classes– serve as a class outline to generate many classes --> 在编译时会根据你的代码,推测生成哪些类。
– specific classes are generated during compile time --> 注意,是在编译时!
Class templates promote code reusability --> 类模板提高了代码的可重复利用性
– reduce program development time
– used for a need to create several similar classes at least one type is generic (parameterized)
当有一个或几个参数都是比较泛化的情况时,可以考虑用类模板。
Terms “class template” and “template class” are used interchangeably.
/************************************************************************/ /* 类模板。 qcy 2016年11月26日14:31:11 * /************************************************************************/ #include <iostream> using namespace std; // 类中含有暂未指定类型的成员,就是类模板。 template <class T> class CPerson { public: CPerson() {} CPerson(T num):number(num) {} ~CPerson() {} void setNumber(T num) { this->number = num; } T getNumber() { return this->number; } private: T number; }; int main () { // 在实例化的时候,就要指明是用什么类型的了。 double d = 1.3; // CPerson cPerson2(d); // 错误。缺少模板的参数列表 CPerson<double> cPerson1; cPerson1.setNumber(19.5); cout<<"cPerson的number:"<<cPerson1.getNumber()<<endl; // 连声明一个空指针,都要指定用什么类型。 CPerson<double> * cPersonPt1 = nullptr; CPerson<char> cPerson3(97); cout<<"cPerson的number:"<<cPerson3.getNumber()<<endl; CPerson<int> cPerson4('A'); cout<<"cPerson的number:"<<cPerson4.getNumber()<<endl; CPerson<char> cPerson5('b'); cout<<"cPerson的number:"<<cPerson5.getNumber()<<endl; system("pause"); return 0; }结果。
cPerson的number:19.5
cPerson的number:a
cPerson的number:65
cPerson的number:b
注意。
1. 在实例化的时候,就要指明是用什么类型的了。
2. 连声明一个空指针,都要指定用什么类型。
模板参数
3 forms of template parameters– type parameters
– non-type parameters
– template parameters
1. A type parameter defines a type identifier
– when instantiating a template class, a specific datatype listed in the argument list substitute for the type identifier
– either class or typname must precede a template type parameter
2. A non-type parameter can be
– integral types: int, char, and bool
– enumeration type
– reference to object or function
– pointer to object, function or member
A non-type parameter cannot be
– floating types: float and double
– user-defined class type
– type void
Good e.g.
template <int A, char B, bool C> // A也就是一种类型了。在这里,就是int型了。
class G1 { //... };
template <float* D, double& E> // D从此以后,就是float * 类型
class G2 { //... };
Bad e.g.
template <double F>
class B1 { //... }; //cannot be double
template <PhoneCall P>
class B2 { //... }; //cannot be class
3. A template parameter may have a default argument.
e.g.
template <class T=int, int n=10>
class C3 { //... };
C3< > a; // 默认把T当int型,但是要加“<>”
C3 b; //error: missing < >
C3<double,50> c;
C3<char> d;
C3<20> e; //error: missing template argument!要指定T的类型,这里的20只是说int n的n要是20。编译器没有这么聪明……
自己用模板写一个Stack
【非常重要的一个例子】/************************************************************************/ /* 我自己写的一个stack qcy 2016年12月25日10:21:43 */ /************************************************************************/ #include <iostream> using namespace std; template<typename T,int MAX_SIZE> class MyStack { public: MyStack(); ~MyStack(); bool full() { return (top == MAX_SIZE); } bool empty() { return (top == 0); } bool push(T obj) { bool result; if (full()) { cout<<"stack full"<<endl; result = false; } else { elements[top] = obj; top++ ; result = true; } return result; } T pop() { if (empty()) { cout<<"stack empty"<<endl; exit(-1); // 如果不想退出呢? } else { top--; T temp = elements[top]; return temp; } } private: int top; T elements[MAX_SIZE]; }; template<typename T,int MAX_SIZE> // 【注意】 在实例化的时候,这里不是写int,是写MAX_SIZE MyStack<T,MAX_SIZE>::MyStack() { top = 0; } template<typename T,int MAX_SIZE> MyStack<T,MAX_SIZE>::~MyStack() { } int main () { MyStack<double,5> stack; int i = 0; while (!stack.full()) { stack.push(i); i++; } while (!stack.empty()) { cout<<stack.pop()<<" "; } cout<<"\n"; MyStack<char,10> stack2; char c = 'a'; while (!stack2.full()) { stack2.push(c); c++; } while (!stack2.empty()) { cout<<stack2.pop()<<" "; } cout<<"\n"; system("pause"); return 0; }运行结果。
4 3 2 1 0
j i h g f e d c b a
(first in, last out.)
注意。
1. 构造函数如果在类的外面写,一定要指定模板参数列表!
// 【注意】 在实例化的时候,这里不是写int,是写MAX_SIZE
template<typename T,int MAX_SIZE>
MyStack<T,MAX_SIZE>::MyStack() // 要统一。T是一种类型,MAX_SIZE也是一种类型了…
{
// ...
}
2. template<typename T,int MAX_SIZE> // 这里的MAX_SIZE也是int型了。后面实例化的时候,竟然可以传一个常数进来!
class MyStack
{ // ...
}
3. 不能把构造函数写到另一个CPP文件单独里面去…!好像暂时我发现是这样……
模板中的友元和继承
Friend functions can be used with template classes– same as with ordinary classes
– simply requires proper type parameters
– common to have friends of template classes, especially for operator overloading
Nothing new for inheritance
Derived template classes
– can derive from template or non-template class
– derived class is naturally a template class
示例。
1. 定义一个基类模板-->注意,这是模板!一个basic的class template。基础的类模板。
template <class T> class TBase { private: T x, y; public: TBase() {} TBase(T a, T b) : x(a), y(b) {} ~TBase() {} T getX(); T getY(); }; template <class T> T TBase<T>::getX() const { return x; } template <class T> T TBase<T>::getY() const { return y; }
2. 从基础类模板,派生一个(非模板的)类(注意,此时派生的东西,不是模板了!已经成为一个类了!)
Derive non-class template from class template --> easy to understand
– behave like normal classes
把所有的T啊、U啊这些模板参数,在代码里都指定类型了。现在,TDerived1就不是一个类模板了!而是一个类!
class TDerived1: public TBase<int> { private: int z; public: TDerived1(int a, int b, int c): TBase<int>(a,b), z(c) {} int getZ() { return z; } };
3. 从基础类模板,派生一个类模板(注意,此时派生的东西还是模板!)
Derive class template from class template
– same as the normal class inheritance
只要还有模板参数没有指定类型,就还是类模板,而不是类。
这就像,只要还没有把抽象基类基类所有的纯虚函数都override实现一遍的话,就还是抽象基类。
template <class T> class TDerived2 : public TBase<T> { private: T z; public: TDerived2(T a, T b, T c): TBase<T>(a,b), z(c) {} T getZ() { return z; } };
4. 从基础类,派生一个类模板(由类生出模板。使用要小心!)
template <class T> class TDerived3 : public TDerived1 { private: T w; public: TDerived3(int a, int b, int c, T d): TDerived1(a,b,c), w(d) {} T getW() { return w; } };
call TDerived1 constructor with known datatypes for parameters!
5. 主函数这样调用。
TBase<int> c1(0,1); cout << "TBase: x=" << c1.getX() << " y=" << c1.getY() << endl; TDerived1 c2(1,3,5); cout << "TDerived1: x=" << c2.getX() << " y=" << c2.getY() << " z=" << c2.getZ() << endl; TDerived2<double> c3(2.2, 4.4, 6.6); cout << "TDerived2: x=" << c3.getX() << " y=" << c3.getY() << " z=" << c3.getZ() << endl; TDerived3<int> c4(3.5, 6.5, 9.5, 12.5); cout << "TDerived3: x=" << c4.getX() << " y=" << c4.getY() << " z=" << c4.getZ() << " w=" << c4.getW() << endl;
结果。
TBase: x=0 y=1 // 类模板的实例化(指定用int型)
TDerived1: x=1 y=3 z=5 // 实例化一个类!已经是类了。
TDerived2: x=2.2 y=4.4 z=6.6 // 实例化一个类模板!(还是模板!)指定用double型!
TDerived3: x=3 y=6 z=9 w=12 // 实例化一个由类派生的类模板!注意,是实例化的模板!所以要指定类型!
// 此时,指定的是int型,所以,
// template <typename T1, ... > 的第一个模板参数 T1,在编译时就已经变成了int!
// 因此,后面的12.5也会被转为12
补充:枚举类型
枚举类型……相关文章推荐
- C++ template Day Day Up 第一天 模板编译模式
- C++之模板(Template)
- c++ template笔记(3)非类型模板参数nontype template parameters
- 【C++模板】特化与偏特化 template [partial] specialization
- 【C++模板】特化与偏特化 template [partial] specialization
- C++之模板(Template)
- 行为模式之模板方法模式(Template Pattern)C++实现
- C++之模板(Template)
- [c++][template]模板基础知识
- C++模板引擎Ctemplate的介绍
- C++ Template----类模板简单阐述
- C++ template - 类模板
- c++模板中的 typename 和 template 关键字用法
- 转@关于c++模板template中的typename 和class的区别
- C++中的模板(template)
- C++ 模板(template)(泛型)入门例子
- C++之模板(Template)
- C++ template - 模板的模板参数
- C++模板template
- 设计模式C++描述----02.模板(Template)模式