模板:
模板是一种通用的描述机制,也就是说,使用模板允许使用通用类型来定义函数或类等,在使用时,通用类型可被具体的类型,如int、double甚至是用户自定义的类型来代替。模板引入一种全新的编程思维方式,称为“泛型编程”或“通用编程”。
为什么要定义模板:
1、模板的的引入大大简化了程序代码量,维持了结构的清晰,大大提高了程序设计的效率。该过程称为“类型参数化”。
2、强类型程序设计中,参与运算的所有对象的类型在编译时即确定下来,并且编译程序将进行严格的类型检查。为了解决强类型的严格性和灵活性的冲突。
(1)带参数宏定义(原样替换)
(2)重载函数(函数名相同,函数参数不同)
(3)模板(将数据类型作为参数)
模板的定义与实例化:
函数模板实际上不是个完整的函数定义,因为其中的类型参数还不确定,只是定义了某些类型的角色(或变量)在函数中的操作形式,因此,必须将模板参数实例化才能使用函数,用模板实例化后的函数也称为模板函数.
1、模板参数实例化才能使用函数,用模板实例化后的函数也称为模板函数。
2、分为隐式实例化和显式实例化(模板特化)
template <class T>
template<typename T>
|
模板不能分成头文件和实现文件,模板的声明和实现必须要在同一个文件里面; 在 .cpp 文件实现模板,要在 main 所在文件 #include ".cpp"
函数模板:
函数模板的定义:
template <模板参数表>
返回类型 函数名(参数列表)
{
//函数体
}
|
模板参数表不能为空,模板参数有两种类型:
class或typename修饰的类型参数,代表一种类型;
非类型参数表达式,必须是int类型,使用已知类型符,代表一个常量
|
#include <iostream>
using namespace std;
//非类型参数只能是int, 模板又提供了一种传递常量的方式
template <typename T,int num> //函数模板声明
T func (T a);
int main()
{
//cout<<func(4)<<endl<<endl; 不能再用隐式实例化
//只能显示实例化
cout<<func <int ,4>(3) <<endl;
return 0;
}
//函数模板的实现
//要求:函数模板的声明和实现必须放在同一个文件里
template <typename T ,int num>
T func (T a)
{
return a * num;
}
|
template_init.cc
//函数模板的实现
//要求:函数模板的声明和实现必须放在同一个文件里面
#include <iostream>
using namespace std;
template <typename T, int num>
T func(T a)
{
return a * num;
}
// 模板不能分成头文件和实现文件,
// 模板的声明和实现必须要在同一个文件里面
#include "template_int.cc"
int main()
{
cout << func<int, 4>(3) << endl;
}
|
函数模板的使用:
1、函数模板的使用规则和普通函数是相同的,在使用函数模板之前,必须对函数模板进行声明,此声明必须在外部进行,也就是说不能在任何一个函数(包括main函数)中声明,声明的格式为:
template <class T1[,class T2,……]>
函数原型
2、和普通函数一样,如果在使用函数模板前对函数模板进行了定义,函数模板的声明可以省略。
#include <iostream>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
cout<<"template <> add()"<<endl;
return a+b;
}
int main()
{
cout<<add(10,20)<<endl; //隐式实例化
cout<<add(1.2,2.3)<<endl;
return 0;
}
|
#include <iostream>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
cout<<"template <> add()"<<endl;
return a+b;
}
//模板特化(不能独立存在,先有通用的形式,才有特化的形式),优先执行
template <>
double add <double> (const double &a,const double &b)
{
cout<<"template <> add <double> ()"<<endl;
return a+b;
}
int main()
{
cout<<add(10,20)<<endl; //隐式实例化
cout<<add(1.2,2.3)<<endl;
return 0;
}
|
函数模板重载:
函数模板支持重载,既可以模板之间重载(同名模板),也可以实现模板和普通函数间的重载,但模板的重载相比普通函数的重载要复杂一点
#include <iostream>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
cout<<"template <> add()"<<endl;
return a+b;
}
//普通函数跟函数模板发生重载时,会优先调用普通函数
int add(const int &a,const int &b)
{
cout<<"int add(const int &,const int &)"<<endl;
return a+b;
}
int main()
{
cout<<add(10,20)<<endl; //隐式实例化
cout<<add(1.2,2.3)<<endl;
return 0;
}
|
//函数模板和函数模板发生重载
#include<iostream>
using namespace std;
template<class T>
T max1(T a,T b) // 为什么max1,因为max和std里面有重名
{
cout<<"template<>max(T,T)"<<endl;
return a>b?a:b;
}
template<class T>
T max1(T a,T b,T c)
{
cout<<"template<>max(T,T,T)"<<endl;
return a>b?(a>c?a:c):(b>c?b:c);
}
int main()
{
int a=1,b=2,c=3;
double a1=1.1,b2=2.2;
cout<<max1(a,b,c)<<endl;
cout<<max1(a1,b2)<<endl;
return 0;
}
|
#include<iostream>
using std::cout;
using std::endl;
// 函数模板和数组的重载
template<class T>
T max(T a,T b)
{
cout<<"max(T,T)"<<endl;
return a>b?a:b;
}
template<class T>
//T max(T *a,T b) // T max(T a[],T b) // 都是错的
T max(T* a,int b)
{
cout<<"max(T*,T)"<<endl;
T tmp = 1>>sizeof(T);
for(int i=1;i!=b;++i)
{
tmp = tmp>a[i]?tmp:a[i];
}
return tmp;
}
int main()
{
int a[] = {1,2,6,4,5};
int a1 = 1,a2 = 2;
cout<<max(a1,a2)<<endl;
cout<<max(a,sizeof(a)/sizeof(int))<<endl;
return 0;
}
|
优先级与执行顺序:
总体来说,一般函数优先于模板函数的执行
#include <iostream>
#include <string.h>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
cout<<"template <> add()"<<endl;
return a+b;
}
#if 0
//模板特化,实现两个字符串相加
template <>
char * add <char *> (char * const &a, char * const &b)
{
cout<<"template <> add <char *> ()"<<endl;
char * tmp = new char[strlen(a)+strlen(b)+1];
strcpy(tmp,a);
strcat(tmp,b);
return tmp;
}
#endif
#if 1
//重载普通函数,实现两个字符串相加
char * add(char * const &a,char * const &b)
{
cout<<"char * add(char * const &,char * const &)"<<endl;
char * tmp = new char[strlen(a)+strlen(b)+1];
strcpy(tmp,a);
strcat(tmp,b);
return tmp;
}
#endif
|
int main()
{
cout<<add(10,20)<<endl; //隐式实例化
cout<<add(1.2,2.3)<<endl;
char * p1 = new char [6];
strcpy(p1,"hello");
char * p2 = new char [6];
strcpy(p2,"world");
char * p3 = add(p1,p2);
cout<<p3<<endl;
return 0;
}
|
类模板:
类模板名<T> 对象名; eg: Stack<int, 10> stack;
************************类模板参数可以给默认值,但函数模板不能给默认值。如果出现非类型参数,则必须是int型。
栈:
#include <iostream>
#include<string>
#include<sstream>
using namespace std;
template <typename T,int num>
class Stack
{
private:
int _top;
T _parr[num];
public:
Stack();
~Stack();
bool full();
bool empty();
bool push(T elem);
bool pop(T &);
int & getPos()
{
return _top;
}
};
template <typename T,int num>
Stack<T,num>::Stack():_top(-1)
{}
template <typename T,int num>
Stack<T,num>::~Stack()
{}
template <typename T,int num>
bool Stack<T,num>::full()
{
return _top == (num-1);
}
template <typename T,int num>
bool Stack<T,num>::empty()
{
return _top == -1;
}
template <typename T,int num>
bool Stack<T,num>::push(T elem)
{
if(!full())
{
_parr[++_top] = elem;
return true;
}
return false;
}
template <typename T,int num>
bool Stack<T,num>::pop(T & t)
{
if(!empty())
{
t = _parr[_top--];
return true;
}
else
return false;
}
|
int test0(void)
{
Stack<int, 10> stackInt;
cout << "开始时stakcInt是否为空?" << stackInt.empty() << endl;
stackInt.push(5);
cout << "此始时stakcInt是否为空?" << stackInt.empty() << endl;
for(int idx = 1; idx !=10; ++idx)
{
stackInt.push(idx);
}
cout << "此时stakcInt是否已满?" << stackInt.full() << endl;
for(int idx = 0; idx != 10; ++idx)
{
int elem = 0;
stackInt.pop(elem);
cout << elem << " ";
}
cout << endl;
return 0;
}
int test1(void)
{
Stack<string, 10> stackInt;
cout << "开始时stakcInt是否为空?" << stackInt.empty() << endl;
stackInt.push("aa");
cout << "此始时stakcInt是否为空?" << stackInt.empty() << endl;
for(int idx = 1; idx !=10; ++idx)
{
string s(2, 'a' + idx);
//string类的一个构造函数,表示含有2个元素的string对象,其中每个元素都初始化为后面的字符
stackInt.push(s);
}
cout << "此时stakcInt是否已满?" << stackInt.full() << endl;
for(int idx = 0; idx != 10; ++idx)
{
string elem;
stackInt.pop(elem);
cout << elem << " ";
}
cout << endl;
return 0;
}
int main()
{
test0();
test1();
return 0;
}
|
队:
#include<iostream>
#include<string>
using namespace std;
template<class T,int num>
class queue
{
public:
queue();
~queue();
bool empty();
bool full();
bool push(T elem);
bool pop(T& tmp);
int size();
private:
int _front;
int _real;
T _arr[num];
};
template<class T,int num>
queue<T,num>::queue():_front(0),_real(0){}
template<class T,int num>
queue<T,num>::~queue(){}
template<class T,int num>
bool queue<T,num>::empty()
{
return _front == _real;
}
template<class T,int num>
bool queue<T,num>::full()
{
return _front == (_real+1)%num;
}
template<class T,int num>
bool queue<T,num>::push(T elem)
{
if(!full())
{
_arr[_real] = elem;
_real = (_real+1)%num;
return true;
}
else
return false;
}
|
template<class T,int num>
bool queue<T,num>::pop(T &tmp)
{
if(!empty())
{
tmp = _arr[_front];
_front = (_front+1)%num;
return true;
}
else
return false;
}
template<class T,int num>
int queue<T,num>::size()
{
return (_real-_front+num)%num;
}
int main()
{
queue<int,10> q1;
q1.push(3);
q1.push(5);
int tmp;
cout<<q1.size()<<endl;
q1.pop(tmp);
cout<<tmp<<endl;
cout<<"----------------------"<<endl;
queue<string,5> q2;
q2.push("hello");
q2.push("world");
cout<<q2.size()<<endl;
string tmpString;
q2.pop(tmpString);
cout<<q2.size()<<" "<<tmpString<<endl;
return 0;
}
|
函数成员模板:
可以将函数模板作为另一个类(必须是模板类)的成员,称为函数成员模板,其用法和普通成员函数类似
#include <iostream>
using namespace std;
template <typename T>
class Test
{
public:
template <typename D>
T convert(D);
};
//有些编译器成员函数模板实现放在类外不能编译通过
template <typename T>
template <typename D>
T Test<T>::convert(D t)
{
return T(t); //类型转换
}
|
int main()
{
Test<int> test;
cout<<test.convert(3.14)<<endl; //隐式实例化
cout<<test.convert<double>(3.14)<<endl; // 显式实例化
return 0;
}
|
嵌套模版类的模版类:
类模板不等于类定义,需要实例化或特化来生成类实例
● 模板的套嵌可以理解成在另外一个模板里面定义一个模板。以模板(类,或者函数)作为另一个模板(类,或者函数)的成员,也称成员模板。
● 成员模版是不能声明为virtual的。
#include <iostream>
using namespace std;
template <typename T,typename T1>
class Outside
{
public:
template <typename D>class Inside; //模板类的前向声明
private:
Inside<T1> t;
public:
Outside(T1 x):t(x)
{}
void disp();
template <typename D>
class Inside
{
private:
D r;
public:
Inside(D x):r(x){};
void disp();
};
};
template <typename T,typename T1>
void Outside<T,T1>::disp()
{
cout<<"Outside()"<<endl;
t.disp();
}
|
template <typename T,typename T1>
template <typename D>
void Outside<T,T1>::Inside<D>::disp()
{
cout<<"Inside()"<<endl;
cout<<Outside<T,T1>::Inside<D>::r<<endl;
}
int main()
{
Outside<int,double>::Inside<double> obj(3.5); //类外定义嵌套类,要求嵌套类是public
obj.disp();
Outside<int,double> obj1(2.1); //类外定义嵌套类,要求嵌套类是public;
obj1.disp();
return 0;
}
//模板类的成员函数也可以在定义外实现
//但必须是在所有类定义的外边,不能放在Outside内Inside外去实现.
|
模版做参数:
模板包含类型参数(如class Type)和非类型参数(如int NUM,NUM是常量),实际上,模板的参数可以是另一个模板
template<template <class T1> class T2, class T3,int Num>;
#include <iostream>
using namespace std;
template <typename T,int num>
class A
{
private:
T num1;
public:
A(T a=num):num1(a)
{
cout<<"A()"<<endl;
}
int ReturnNum();
};
template <typename T,int num>
int A<T,num>::ReturnNum()
{
return num1;
}
template <template<typename T,int num> class B, typename T1, int N>
//第一个参数声明的是一个模板类
void disp()
{
cout<<"disp()"<<endl;
B<T1,N> ob;
cout<<ob.ReturnNum()<<endl;
}
|
int main()
{
disp<A,int,8>(); //第一个参数用模板类A来实例化
return 0;
}
|
模板是C++引入的新特性,也是标准模板库STL的基础,模板有函数模板和类模板之分,两种应用有很多相似之处。学习模板,最重要的是理解模板定义(函数模板定义、类模板定义)与具体定义(函数定义和类定义)的不同,模板不是定义,要通过实例化(通过模板)或特化(避开模板)来生成具体的函数或类定义,再调用函数或创建类的对象。
模板支持嵌套,这就是说可以在一个模板里面定义另一个模板。以模板(类,或者函数)作为另一个模板(类,或者函数)的成员,也称成员模板。同时,模板也可以作为另一个模板的参数,出现在类型参数表中。
|