1.1 简介
1、简介
C++提供了两种模板机制,函数模板和类模板。
使用范围:模板的声明或定义只能在全局或类范围进行,不能在局部范围(如函数)内进行。
使用目的:使用模板是为了能够让程序员编写与类型无关的代码。
函数模板和模板函数区别:函数模板是一个模板,其中用到通用类型参数,不能直接执行;模板函数是一个具体的函数,它是一个具体执行的函数,由编译系统在遇到具体函数调用时生成,可执行。
1.2 函数模板
1、函数模板格式
template <类型形参表或称模板参数列表> //类型函数声明
返回类型 函数名(形参表)
{
函数体;
}
注意:
模板参数表:定义在类或函数定义中用到的类型或值。
类型形参表:可以包含基本数据类型,也可以包含类类型。类型形参需要加class或typename关键字(两者等价)
类型形参表的参数必需是唯一的,不能有重名的。
template声明语句和函数模板声明之间不允许有其他语句。
2、函数模板实例化为模板函数
函数模板不能直接执行需要实例化为模板函数后才能执行。
编译系统发现有一个函数调用“函数名(实参表)”或“函数名<类型实参表>(实参表)”时,c++将根据“实参表”中的类型生成一个重载函数,即模板函数。
举例:
函数模板:
template <class T> //T 的名字也为其他 T abs(T x) { if (x < 0) return -x; return x; }
//调用函数模板生成模板函数
cout << abs(-1) << endl;//输出结果为1
生成的模板函数如下:
int abs(int x)
{
if(x<0) return -x;
return x;
}
3、函数模板本身可以重载
1.3 类模板
1、简介
类模板允许用户为类定义一种模式,使得类中的某些数据成员、成员函数的参数或成员函数的返回值能取任意类型。类模板的成员函数被认为是函数模板。
2、定义类模板格式
template <类型形参表> //类型参数声明
class 类名
{
类模板的代码
}
template<类型形参表> //定义在类模板之外的函数必需以关键字template开始
返回类型 类名 类型名表::成员函数n(形参表)
{
成员函数定义体
}
3、实例化类模板
类模板不能直接使用,必需先实例化为相应的模板类。定义类模板之后,创建模板类的格式如下:
类模板名 <类型实参表> 对象表;
类型实参表与类模板中的类型形参表相匹配。
4、类模板举例
#include <iostream>
#include <map>
#include "string"
//#include <memory>
using namespace std;
template <typename T>
class Array //类模板
{
int size;
T *p;
public:
Array();//默认构造函数
Array(int n);//重载构造函数
T &operator[](int) const;//[]重载函数
};
//默认构造函数
template <typename T>
Array<T>::Array()
{
size = 10;
p = new T[size];
}
//重载构造函数
template <typename T>
Array<T>::Array(int n)
{
size = n;
p = new T[size];
}
//下标运算符重载函数
template<typename T>
T &Array<T>::operator[](int i)const
{
if (i >= 0 && i < size)
{
return p[i];
}
}
class Student
{
int no;
char name[10];
public:
Student() {}
Student(int n,const char *s)//注意要有const 否则”Tom“无法传参
{
no = n;
strcpy_s(name, s);//使用strcpy 为字符串数组赋值
}
Student &operator=(Student &s) //赋值重载构造函数
{
no = s.no;
strcpy_s(name, s.name);
return *this;
}
void display()
{
cout << "学号:" << no << ",姓名:" << name << endl;
}
};
int main()
{
Array<int> a(5);
for (int i=0;i<5;i++)
{
a[i]=i+1;//调用运算符重载函数
cout << a[i] << " ";
}
cout << endl;
cout << "学生列表" << endl;
Array<Student> b(3);
//int c = 1;
Student x(1, "Tom"), y(2, "Marry"), z(3, "John");
b[0] = x; b[1] = y; b[2] = z;
for (int i=0;i<3;i++)
{
b[i].display();
}
}
5、类模板作为函数参数
//类模板
template <class T> //T 的名字也为其他
class A
{
T x;
public:
A(T a) { x = a; }
T abs()
{
if (x < 0) return -x;
else return x;
}
};
//函数模板中模板类作为形参
template <class T>
void fun(A<T> x)
{
cout << x.abs() << endl;
}
int main()
{
//建立对象
A<int> s1(-5);
A<double> s2(-5.8);
fun(s1);
fun(s2);
}
5、类模板中也可以使用非类型参数,即值参数
template<class T,int size>
class A
{
.....
}
实例化:
A<int,3> s;
6、模板与静态函数
类模板中定义静态函数,则该模板类的所有对象共享一个静态数据成员。
7、类模板的友元函数
一个类模板中可以设计友元函数,友元函数的形参可以是类模板或类模板的引用。如果在类模板中设计与参数类型无关的友元函数,那么在类外面实现时也不能省略template类型参数声明,否则将其看成是一个普通全局函数。
template <class T> //T 的名字也为其他
class A
{
public:
T x;
A() { }
A(T i):x(i){ }
friend void f1();//与参数类型无关的友元函数
friend void f2(A<T> &);//与参数类型有关的友元函数
};
template<class T>
void f1(){ cout << "f1" << endl; }
template<class T>
void f2(A<T> &a) { cout << "f2:x " << a.x << endl; }
int main()
{
A<double> a(1.2);
f1<int>();//f1是模板类A<int>的友元函数
f1<double>();//f1是模板类A<double>的友元函数
f2<double>(a);//f2是模板类A<double>的友元函数
}
1.4 可变参数模板
1、概况
c++ 11 可变参数模板,对参数进行了高度泛化,能表示任意个数,任意类型的参数。
c++11 可变参数模板,就是一个接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包。
存在两种 参数包:
模板参数包:表示0个或多个模板参数
函数参数包:表示0个或多个函数参数
2、格式
template<class... >
template<typename...>
举例:
#include <iostream>
#include <map>
//#include <memory>
using namespace std;
template <typename T,typename...Args>//foo是可变参数函数模板 有一个名为T的类型参数和一个名为Args的模板参数包
void foo(const T &t,const Args& ...rest) //foo函数列表包含一个const&类型的参数 指向T的类型,名为rest的函数参数包
{
cout << sizeof...(rest) << endl;//输出函数参数的数目
};
int main()
{
int i = 0; double d = 3.14; string s = "hello";
foo(i,s,42,d);//包中有三个参数
foo(s, 42, d);//包中有两个参数
foo(d, s);//包中有一个参数
foo("hi");//空包
}
3、统计可变数目参数的个数
#include <iostream>
using namespace std;
template <class... T>
void f(T... args)
{
cout << sizeof...(args) << endl;
};
int main()
{
f();
f(1, 2);
f(1, 2.5, "");
}