函数模板:
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体
指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数
体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中
定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类
型,从而实现了不同函数的功能
模板:将 算法 与 数据类型 相分离,专注于算法的实现
一、函数模板定义
// 函数模板定义:用 template 将函数声明成函数模板
template <typename T>
void mySwap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10;
int b = 20;
mySwap(a, b); // 可以隐式调用 函数模板 编译器会自己判断变量的类型
// cout << "a = " << a << ", b = " << b << endl;
myPrint(a, b);
// 变量类型进行参数化:如何传类型?
mySwap<int>(a, b);
// cout << "a = " << a << ", b = " << b << endl;
myPrint<int>(a, b);
char c1 = 'a';
char c2 = 'b';
mySwap<char>(c1, c2);
// cout << "a = " << c1 << ", b = " << c2 << endl;
myPrint<char>(c1, c2);
double d1 = 1.2;
double d2 = 2.3;
mySwap<double>(d1, d2);
// cout << "a = " << d1 << ", b = " << d2 << endl;
myPrint<double>(d1, d2);
return 0;
}
二、函数模板的性质
1、函数模板调用和普通函数的区别:
普通函数支持类型的隐式转换,函数模板不支持类型的隐式转换
如果 普通函数在被调用时 如果实参类型 与 形参类型不一致, 会自动转换
2、函数模板和普通函数可以共存
1、当函数模板和普通函数都可以调用的时候,优先调用普通函数
2、如果想调用函数模板,可以用空参数列表 <> 进行说明
3、当函数模板可以提供更优选择的时候,优先调用函数模板
4、函数模板也是可以重载的
template <typename T>
int myAdd(T a, T b)
{
return a+b;
}
template <typename T>
int myAdd(T a, T b, T c)
{
return a+b+c;
}
三、模板机制
编译器并不是把函数模板处理成能够处理任意类的函数;
编译器从函数模板通过具体类型产生不同的函数;
编译器会对函数模板进行两次编译:
第一次:在声明的地方对模块代码本身进行编译
第二次:在调用的地方对参数替换后的代码进行替换
说明:通过 函数模板 产生的函数 被称为 模板函数
四、类模板
类模板用于实现类所需数据的类型参数化
类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示
和算法不受所包含的元素类型的影响
1、声明一个类模板
template <typename T>
class A
{
public:
A(T a)
{
this->a = a;
}
void print ()
{
cout << "a = " << a << endl;
}
private:
T a;
};
2、类模板对象做函数参数传递的时候, 要写一个 具体的 模板类对象
void myPrint(A<int> &a)
{
a.print();
}
void myPrint(A<double> &a)
{
a.print();
}
3、使用函数模板
template <typename T>
void printA(A<T> &a)
{
a.print();
}
4、函数模板可以隐式调用,但类模板不可以隐式调用,必须说明模板类型
A<int> a(10);
A<double> d(1.2);
myPrint(a);
myPrint(d);
printA(a);
printA(d);
5、派生一个具体的类,继承自一个具体的模板类
class B : public A<int>
{
public:
B(int a, int b): A(a)
{
}
private:
int b;
};
6、派生一个类模板
template <typename T>
// class C:public A<T>
class C: public A<int>
{
public:
C(int a, T c):A(a)
{
}
private:
T c;
};
五、类的实现
1、类的实现放在类的内部
#include <iostream>
using namespace std;
template <typename T>
class Complex
{
// 友元函数写到类的内部
// 虽然写到了类的内部,但是该函数还是友元函数,不是类的内部函数
friend ostream &operator << (ostream &out, Complex &c)
{
out << c.a << " + " << c.b << "i";
return out;
}
friend Complex mySub(Complex &c1, Complex &c2)
{
Complex tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
public:
Complex(T a = 0, T b = 0)
{
this->a = a;
this->b = b;
}
Complex operator +(Complex &c)
{
Complex tmp(a + c.a, b + c.b);
return tmp;
}
private:
T a;
T b;
};
int main()
{
Complex<int> c1(1, 2), c2(3, 4), c;
// mySub的实现 虽然是写到了类的内部,但是它还是一个友元函数
// 如果是类的内部函数 调用如下: c1.mySub(c2)
// 但因为 mySub 是友元函数,实际是一个外部的全局函数,所以只能用如下调用:mySub(c1, c2);
c = mySub(c1, c2);
cout << c1 << endl;
cout << c << endl;
c = c1 + c2;
cout << c << endl;
return 0;
}
类的成员函数在类的外部实现时,需要将成员函数写成函数模板
#include <iostream>
using namespace std;
// 声明类
template <typename T>
class Complex;
// 函数声明
template <typename T>
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);
template <typename T>
class Complex
{
friend ostream &operator<< <T>(ostream &out, Complex<T> &c);
friend Complex<T> mySub <T>(Complex<T> &c1, Complex<T> &c2);
public:
Complex(T a = 0, T b = 0);
Complex operator +(Complex &c);
private:
T a;
T b;
};
template <typename T>
Complex<T>::Complex(T a = 0, T b = 0)
{
this->a = a;
this->b = b;
}
template <typename T>
Complex<T> Complex<T>::operator +(Complex &c)
{
Complex tmp(a + c.a, b + c.b);
return tmp;
}
template <typename T>
ostream & operator << (ostream &out, Complex<T> &c)
{
out << c.a << " + " << c.b << "i";
return out;
}
template <typename T>
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2)
{
Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
int main()
{
Complex<int> c1(1, 2), c2(3, 4), c;
c = mySub(c1, c2);
cout << c1 << endl;
cout << c << endl;
c = c1 + c2;
cout << c << endl;
return 0;
}
3、类的实现放在类的外部(不同文件中)
只要将函数实现" cpp "文件,改名为 " hpp "作为 main.cpp
template <typename T>
Complex<T>::Complex(T a = 0, T b = 0); //外部原型
//前面的 Complex<T> 说明是 Complex 的成员函数
Complex (T a = 0, T b = 0); //类中声明
template<typename T>
Complex<T> Complex<T>::operator+(Complex &c);//外部原型
第一个是 Complex<T> 是返回值
第二个是 Complex<T> 说明是 Complex 的成员函数
Complex operator+(Complex &c); //类中声明
template <typename T>
ostream operator << (ostream &out, Complex<T> &obj); //类外部原型
friend ostream operator << <T>(ostream &out, Complex<T> &obj);//类中声明
template <typename T>
Complex<T> muSub(Complex<T> &c1, Complex<T> &c2);//外部原型
friend Complex<T> mySub<T>(Complex<T> &c1, Complex<T> &c2);//类中声明
六、类模板中的 static 关键字
同一类型 的 模板类的对象 共享 static
不属于同一类型的 模板类的对象 不共享 static
template <typename T>
T A<T>::sa = 1;
class A_int
{
public:
int a;
static int sa;
};
int A_int::sa = 1;
class A_double
{
public:
double a;
static double sa;
};
double A_double::sa = 1;
int main()
{
A<int> a;
a.sa = 10;
A<double> d;
cout << d.sa << endl; //sa = 1
cout << A<int>::sa << endl; //sa = 10
return 0;
}