模板和标准模板库(STL)
---------------------------------------------------------------
实现与类型无关的算法和数据结构,需要将实现中的类型参数化,允许用户根据它的需要指定不同的类型
一、 函数模板
1、 定义
template 模板形参表
template<typename 类型形参1,typename 类型形参2,….>
返回类型 函数模板名(调用形参表){函数体}
在返回类型、调用形参表和函数体中,所有需要类型的地方都可以引用模板形参表的类型形参
例如:
typeplate<typename A,typename b,typename _C>
A function(b arg){
_C var;
….
}
2、 使用
函数模板名<类型实参1,类型实参2,…..>(调用实参表)
int i= function<int,double,string>(1.23);
/*
int function(double arg){
stringvar;
…….
}
*/
char c=function<char string,Student>(“hello”);
/*
char function(string arg){
Studentvar;
}
*/
举例:
#include <iostream>
using namespace std;
// 函数模版/模版函数
template<typename T /*类型形参*/>
T max (T x,T y) {
return x < y ? y : x;
}
int main(void)
{
int a=123,b=456;
cout << ::max<int> (a,b) << endl; //456
double c=1.3,d=4.6;
cout << ::max<double> (c,d) << endl;
string e = "hello",f = "world";
cout << ::max<string> (e,f) << endl; //world
char g[] = "hello",h[] = "world";
cout << ::max<string> (g,h) << endl;
return 0;
}
3、类型参数
1)类型形参
A :前面必须带有typename或class关键字;
B:命名规则:以字母或下划线开始,由字母、下划线或数字组成,不能包含标点符号、数学符号等
2)类型实参
A:传递给函数模板的类型实参既可以是基本类型(char/int.double),也可以是类类型(student/teacher),甚至可以是有其它模板实例化的类型(string)。
#include<string>
Typedef basic_string <char> string;
B:传递给函数模板的类型实参必须满足模板实现的具体需求,否则将引发编译错误
举例:
1#include<iostream>
2using namespace std;
3template<typename T>
4 Tadd(T x,T y){
5 return x+y;
6 }
7class Integer{
8public:
9 Integer(intarg=0):m_var(arg){}
10 friend ostream& operator<<(ostream& os,Integer const&i){
11 return os<<i.m_var;
12 }
13 Integer const operator+(Integer const &rhs)const{
14 return m_var+rhs.m_var;
15 }
16private:
17 int m_var;
18};
19int main(void){
20 cout<<add<int>(123,456)<<endl;
21 cout<<add<double>(1.3,4.6)<<endl;
22 cout<<add<string>("hello,","world!")<<endl;
23 //cout<<add<char const*>("hello,","world!")<<endl;指针不能加减
24 cout<<add<Integer>(123,456)<<endl;//用运算符重载实现类的加减
25 return 0;
26 }
tarena@tarena-virtual-machine:~/day48$./a.out
579
5.9
hello, world!
579
4、延迟编译
每个函数模板事实上至少要被编译两次,一次是在编译器“看到”该函数模板被定义的时候,这时的编译器只对该函数模板做一般性语法检查(与类型无关),然后生成关于该函数模板的内部表示。当编译器“看到”该函数模板被调用时,再用所提供的类型实参,结合之前生成内部表示中的类型形参,做与类型相关的语法检查,并生成该函数模板的二进制指令代码。
1#include<iostream>
2using namespace std;
3class A{
4public:
5 typedef unsigned int uint;
6 class B{};
7};
8template<typename T>
9void foo(void){
10 /*typename*/ T::uint u;//会报错,应该加上typename
11 /*typename*/ T::B b;
12 }
13int main(void){
14 A::uint u;
15 A::B b;
16 foo<A>();
17 return 0;
18 }
5、隐式推断
如果函数模板调用参数(圆括号里的参数)的类型相关于该模板的模板参数(尖括号里的参数),那么在调用该函数模板时,即使不显示指定模板参数,编译器也有能力根据调用参数的类型隐式推断出正确的模板参数类型,这就叫函数模板参数的隐式推断。但是,要注意如果编译器隐式推断的类型和程序设计者所期望类型不一致,有可能导致逻辑错误
(返回值不参与隐式推断)
#include <iostream>
using namespace std;
// 函数模版/模版函数
template<typename T /*类型形参*/>
T max (T x,T y) {
return x < y ? y : x;
}
int main(void)
{
int a=123,b=456;
//cout << ::max<int> (a,b) << endl; //456
cout << ::max (a,b) << endl; //隐式推断
double c=1.3,d=4.6;
//cout << ::max<double> (c,d) << endl;
cout << ::max (c,d) << endl;
string e = "hello",f = "world";
//cout << ::max<string> (e,f) << endl; //world
cout << ::max (e,f) << endl;
char g[] = "hello",h[] = "world";
//cout << ::max<string> (g,h) << endl;
cout << ::max (g,h) << endl; //这样不行,打印hello和world不一定,按随机分配内存的大小来比较
return 0;
}
举例:
1#include<iostream>
2#include<typeinfo>
3using namespace std;
4template<typename T>
5void foo(T x,T y){
6 cout<<"foo:"<<typeid(x).name()
7 <<''<<typeid(y).name()<<endl;
8 }
9template<typename T>
10void bar(T const& x,T const& y){
11 cout<<"bar:"<<typeid(x).name()
12 <<' '<<typeid(y).name()<<endl;
13 }
14template<typename R,typename T>
15 Rhum(T x){
16 Ry;
17 cout<<"hum:"<<typeid(x).name()
18 <<''<<typeid(y).name()<<endl;
19 return y;
20 }
21void f1(int x,int y){}
22void f2(double x,double y){}
23int main(){
24 int a,b;//T-->int
25 foo(a,b);//i i
26 double c,d;//T-->double
27 bar(c,d);//d d
28 char e[256],f[256];
29 foo(e,f);//T-->Pc(首地址是字符指针) Pc Pc
30 bar(e,f);//引用是引用数组整体 A256_c A256_c
31/* cout<<sizeof(e)<<endl;//256
32 char(*p)[256]=&e;//数组指针,指针p指向一个数组
33 e[0]='C';// *(e+0)='C' */
34/*笔试题*/
35 foo("hello","tang");// PKc PKc
36 //bar("hello","hao");// A6c A4c 出错,因为类型不一样,隐式推断T 前后应该一样
37 bar<string>("hello","zi");//Ss Ss 可以
38 bar(string("hello"),string("zi"));//可以
39 f1(a,c);//c:double -->int
40 f2(a,c);//a:int -->double
41 //foo(a,c);?
42 //c:double-->int,T=int ?
43 //a:int -->double,T=double ?
44 //会报错,因为隐式转换和隐式推断不能同时出现
45 foo((double)a,c);//d d
46 foo(a,(int)c);//i i
47 foo<double>(a,c);//d d
48 foo<int>(a,c);//i i
49
50 //a=hum(c);返回值的类型不参与隐式推断
51 a=hum<int>(c);//d i
52 return 0;
53 }
6、重载
与普通函数一样,函数模板也可以形成重载关系,类型约束性越强的版本越被优先选择
举例:
1#include<iostream>
2using namespace std;
3template<typename T>
4 Tconst& max(T const& x, T const& y){
5 return x<y?y:x;
6 }
7template<typename T>
8T* const& max(T* const& x, T* const& y){
9 return *x<*y?y:x;
10 }
11int main(void){
12 int a=132,b=456;
13 cout << ::max(a,b) << endl; //123
14 cout << *::max(&a,&b) << endl; //123
15 return 0;
16 }
另一个例子:
#include <iostream>
#include <typeinfo>
#include <cstring>
using namespace std;
//两个任意类型值的最大值
template <typename T>
T const& max (T const& x,Tconst& y) {
cout << "<1" << typeid (x).name () <<'>' << flush;
return x < y ? y : x;
}
//两个任意类型指针所指向目标的最大值
template <typename T>
T* const& max (T* const& x,T*const& y) {
cout << "<2" << typeid (x).name () <<'>' << flush;
return *x < *y ? y : x;
}
//两个字符指针所指向字符串的最大值
char const* const& max(char const*const& x,
char const* const& y) {
cout << "<3" << typeid (x).name () <<'>' << flush;
return strcmp (x,y) < 0 ? y : x ;
}
//三个任意类型值的最大值
template <typename T>
T const& max (T const& x,Tconst& y,
Tconst& z) {
cout << "<4" << typeid (x).name () <<'>' << flush;
return ::max (::max (x,y),z);
}
//不在重载的选择范围内
/*char const* const& max(char const*const& x,
char const* const& y) {
cout << "<3" << typeid (x).name () <<'>' << flush;
return strcmp (x,y) < 0 ? y : x ;
}*/
int main(void)
{
int a=123,b=456;
cout << ::max (a,b) << endl;
cout << *::max (&a,&b) << endl;
char const* c = "ab", *d = "abc";
cout << ::max(c,d) << endl;
cout << ::max<>(c,d) << endl;
cout << ::max<char const*>(c,d) << endl;
char const* e = "abcd";
cout << ::max (c,d,e) << endl;
return 0;
}
二、 类模板
类的成员变量、成员函数、成员类型,以及基类中如果包含参数化的类型,那么该类就是一个类模板
class Base{…..};
class Derived:public Base{//基类
public:
intm_mem;//成员变量
charfun(double arg){string var….}//成员函数
typedefchar* PTR;//成员类型
};
1、定义
template 模板形参表
template<typename 类型形参1,typename 类型形参2,….>
class 类模板名[:基类]{
成员变量
成员函数
成员类型
}
2、使用
类模板名<类型实参2,类型实参2,…..>对象;
类模板名<类型实参2,类型实参2,…..>&引用=对象;
类模板名<类型实参2,类型实参2,…..>*指针=&对象;
|<……………………………………………>|
类/类型/作用域
类模板不能隐式推断,必须显示指明类型实参
Derived<Base,int,char,double,string,char>对象(…..);
3、 实例化
编译期 运行期
类模板—实例化->类---实例化—>对象
编译器 处理器
举例:
#include<iostream>
using namespace std;
template<typename T>
class Comparator{
public:
Comparator(T x,T y):
m_x(x),m_y(y){}
Tmin(void)const{
return m_x<m_y?m_x:m_y;
}
Tmax(void)const{
return m_x>m_y?m_x:m_y;
}
private:
Tm_x,m_y;
};
int main(){
int a=123,b=456;
Comparator<int>ci (a,b);
cout<<ci.min()<<' '<<ci.max()<<endl;
double c=3.14,d=4.6;
Comparator<double>cd (c,d);
cout<<cd.min()<<' '<<cd.max()<<endl;
string e="hello",f="world";
Comparator<string>cs (e,f);
cout<<cs.min()<<' '<<cs.max()<<endl;
return 0;
}
tarena@tarena-virtual-machine:~/day48$./a.out
123 456
3.14 4.6
hello world
4、 类型参数
类模板中,所有的成员函数(无论其是否使用类型参数)都是函数模板,都要延迟编译(即在编译器看到调用该函数时编译)。因此,只有那些被调用的成员函数才会被实例化。某些类型虽然没有提供该类模板所需要的全部功能,但照样可以实例化该模板,只要不直接或间接调用那些依赖于未提供功能的成员函数即可。
#include<iostream>
using namespace std;
template<typename T>
class Comparator{
public:
Comparator(T x,T y):m_x(x),m_y(y){}
Tmin(void)const{
return m_x<m_y?m_x:m_y;
}
Tmax(void)const{
return m_x<m_y?m_y:m_x;
}
void foo (void){
cout << "foo" << endl;
}
private:
Tm_x,m_y;
};
class Integer{
public:
Integer(int arg=0):m_var(arg){}
bool operator<(Integer const& rhs)const{
return m_var<rhs.m_var;
}
friend ostream& operator<< (ostream& os,Integer const&i){
return os<<i.m_var;
}
private:
int m_var;
};
int main(void){
int a=123,b=456;
Comparator<int>ci (a,b);
cout << ci.min() <<' ' << ci.max() <<endl;
double c=1.3,d=4.6;
Comparator<double>cd (c,d);
cout << cd.min() <<' ' << cd.max() <<endl;
string e="hello",f="world";
Comparator<string>cs (e,f);
cout << cs.min() <<' ' << cs.max() <<endl;
Comparator<Integer>cn (a,b);
cout << cn.min() <<' '<< cn.max() << endl;
return 0;
}
tarena@tarena-virtual-machine:~/day48$./a.out
123 456
1.3 4.6
hello world
123 456
5、 静态成员变量
类模板的静态成员变量,既不是一个模板的一份实例,也不是一个对象的一个实例,而是在该类模板的每个实例化类中都有一份独立的实例,且为该实例化类所创建的每个对象所共享。
1#include<iostream>
2using namespace std;
3template<typename T>
4class A{
5public:
6 void paddr(void)const{
7 cout<<"非静态:"<<&m_var<<','
8 <<"静态:"<<&s_var<<endl;
9 }
10private:
11 int m_var;
12 static int s_var;
13};
14template<typename T>
15int A<T>::s_var;
16int main(void){
17 A<int> a,b;
18 A<double>c,d;
19 a.paddr();
20 b.paddr();
21 c.paddr();
22 d.paddr();
23 return 0;
24 }
tarena@tarena-virtual-machine:~/day48$ ./a.out
非静态:0xbf94be60,静态:0x804a0d8
非静态:0xbf94be64,静态:0x804a0d8
非静态:0xbf94be68,静态:0x804a0dc
非静态:0xbf94be6c,静态:0x804a0dc
6、 递归实例化
用一个类模板的实例化类型实例化该类模板自身。
构建在空间上具有递归特性的复合结构,如:多维数组,二叉树 等等
template<typename T>class Array{…..};
template<typename T>class List{…..};
template<typename T>class Tree{…..};
Array<Array<int> >二维数组
Array<List<int> >链表数组
List<List<int>>二维链表
举例:
#include<iostream>
using namespace std;
template<typename T>
class Array{
public:
T& operator[](size_t i){
return m_a[i];
}
Tconst& operator[](size_t i)const{
return const_cast<Array&>(*this)[i];
}
Tm_a[3];
};
int main(){
Array<int> a;
/*
a.m_a[0]=10;
a.m_a[1]=20;
a.m_a[2]=30;
*/
a[0]=10;//a.operator[](0)=10
a[1]=20;
a[2]=30;
Array<int> const b=a;
//cout << b.m_a[0] <<' '<< b.m_a[1]<< ''<< b.m_a[2] << endl;
cout << b[0] <<' '<< b[1]<< ' '<< b[2]<< endl;
Array<string> c;
c[0]="北京";
c[1]="上海";
c[2]="南京";
cout << c[0] <<' '<< c[1]<< ' '<< c[2]<< endl;
Array<Array<int> > d;//由数组组成的数组,二维数组
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
d[i][j]=i*3+j+1;
// d.operator[](i).operator[]{j}=...;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++)
cout << d[i][j] <<' ';
cout << endl;
}
return 0;
}
tarena@tarena-virtual-machine:~/day48$./a.out
10 20 30
北京 上海南京
1 2 3
4 5 6
7 8 9