所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。
一 函数模板初识
1) 为什么要有函数模板?
函数业务逻辑一样,但是函数参数类型不一样,引入泛型编程,方便程序员编程。
2) 语法:
template <typename T>
void myswap(T &a,T &b)
{
}
a: tempalte是告诉C++要进行泛编程,看到T不要随便报错。
b :T表示类型
3) 调用
a:显式调用:
myswap(x,y);
myswap(a,b);
b: 自动类推导
myswap(x,y);//根据参数类型来自动匹配
#include
using namespace std;
// 函数的业务逻辑 一样
// 函数的参数类型 不一样
void myswap01(int &a, int &b)
{
int c = 0;
c = a;
a = b;
b = c;
}
void myswap02(char &a, char &b)
{
char c = 0;
c = a;
a = b;
b = c;
}
template
void myswap(T &a, T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout << "hello ....我是模板函数 欢迎 calll 我" << endl;
}
void main()
{
{
int x = 10;
int y = 20;
myswap(x, y); //1 函数模板 显示类型 调用
myswap(x, y); //2 自动类型 推导
printf("x:%d y:%d \n", x, y);
}
{
char a = 'a';
char b = 'b';
myswap(a, b); //1 函数模板 显示类型 调用
myswap(a, b);
printf("a:%c b:%c \n", a, b);
}
}
4 函数模板作函数参数
#include
using namespace std;
//让int数组进行排序
template
int mySort(T *array, T2 size)
{
T2 i, j ;
T tmp;
if (array == NULL)
{
return -1;
}
//选择
for (i=0; i
int myPrint(T *array, T2 size)
{
T2 i = 0;
for (i=0; i(buf, len);
myPrint(buf, len);
}
cout<<"hello..."<
5 函数模板遇上函数重载
1) 函数模板和普通函数区别:
函数模板不允许自动类型转化,普通函数能够进行自动类型转换
int a = 10;
char c = 'z';
myswap(a, c); // 普通函数的调用: 可以进行隐式的类型转换
myswap(c, a); //这个是时候只会调用普通函数
2)函数模板遇上普通函数时的调用规则:
a:函数模板可以像普通函数一样被重载
b:当函数模板和普通函数都符合调用时,C++编译器优先考虑普通函数
c:如果函数模板可以产生一个更好的匹配,那么选择模板
d:可以通过空模板实参列表的语法限定编译器只通过模板匹配,例如:myswap<>(a,b).
#include "iostream"
using namespace std;
int Max(int a, int b)
{
cout<<"int Max(int a, int b)"< b ? a : b;
}
template
T Max(T a, T b)
{
cout<<"T Max(T a, T b)"< b ? a : b;
}
template
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<(a, b)< 类型列表
cout<
三 编译器模板机制剖析
1 编译器并不是把函数模板处理成能够处理任意类的函数
2 编译器从函数模板通过具体类型产生不同的函数
3 编译器会对函数模板进行两次编译: 在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。
四 类模板初识
1 为什么需要类模板
1)有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同
2) 类模板用于实现类所需数据的类型参数化
3) 类模板在表示如数组、表、图等数据结构显得特别重要)
2 单个类模板语法
template <\typename type>
class Tclass{
//至少有一个成员变量为type类型
};
template<\typename T>
class A //A就是一个模板类(或类模板)
{
public:
A(T t)
{
this->t = t;
}
T &getT()
{
return t;
}
private:
T t; };
void main()
{
//模板了中如果使用了构造函数,则遵守以前的类的构造函数的调用规则
A a(100); //需要进行类型具体化否则报错。
a.getT();
printAA(a);
return ; }
3 从模板类派生普通类
子类从模板类继承的时候,需要让编译器知道父类的数据类型具体是什么
(数据类型的本质:固定大小内存块的别名)A<\int>
4 从模板类派生模板类
template <\typename T>
class C :public A<\T>
{
};
#include
using namespace std;
//模板类
template
class A
{
public:
A(T a)
{
this->a = a;
}
public:
void printA()
{
cout << "a: " << a << endl;
}
protected:
T a;
};
//从模板类 派生了 普通类
// 模板类派生时, 需要具体化模板类. C++编译器需要知道 父类的数据类型具体是什么样子的
//=====> 要知道父类所占的内存大小是多少 只有数据类型固定下来,才知道如何分配内存
class B : public A
{
public:
B(int a=10, int b=20) : A(a)//如果父类调用了有参构造函数需要显示初始化,使用对象初始化列表
{
this->b = b;
}
void printB()
{
cout << "a:" << a << " b: " << b << endl;
}
private:
int b;
};
//从模板类 派生 模板类
template
class C : public A
{
public:
C(T c, T a) : A(a)
{
this->c = c;
}
void printC()
{
cout << "c:" << c < c1(1, 2);
c1.printC();
system("pause");
}
//类模板 做函数参数
//参数 ,C++编译器 要求具体的类 所以所 要 A &a
void UseA( A &a )
{
a.printA();
}
void main61()
{
//模板类(本身就是类型化的)====具体的类=====>定义具体的变量
A a1(11), a2(20), a3(30); //模板类是抽象的 ====>需要进行 类型具体
UseA(a1);
UseA(a2);
UseA(a3);
cout<<"hello..."<
5 知识体系
1)所有类模板函数都写在类的内部
#include
using namespace std;
template
class Complex
{
friend Complex MySub(Complex &c1, Complex &c2)
{
Complex tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
friend ostream & operator<<(ostream &out, Complex &c3)
{
out << c3.a << " + " << c3.b << "i" << endl;
return out;
}
public:
Complex(T a, T b)//在类的内部就不用写template
{
this->a = a;
this->b = b;
}
Complex operator+ (Complex &c2)
{
Complex tmp(a+c2.a, b+c2.b);
return tmp;
}
void printCom()
{
cout << "a:" << a << " b: " << b << endl;
}
private:
T a;
T b;
};
//运算符重载的正规写法
// 重载 << >> 只能用友元函数 ,其他运算符重载 都要写成成员函数 , 不要滥用友元函数
void main()
{
//需要把模板类 进行具体化以后 才能定义对象 C++编译器要分配内存
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;
//滥用友元函数
{
Complex c4 = MySub(c1, c2);
cout << c4 << endl;
}
cout<<"hello..."<
2)所有的类模板函数写在类的外部,在一个cpp中
#include
using namespace std;
//友元函数:友元函数不是实现函数重载(非<<、>>)
//需要在类前增加类的前置声明函数的前置声明
template
class Complex ; //类的前置声明
template
Complex MySub (Complex &c1, Complex &c2);
template
class Complex
{
friend Complex MySub (Complex &c1, Complex &c2);
friend ostream & operator<< (ostream &out, Complex &c3);//解决方法在此处加
public:
Complex(T a, T b);
void printCom();
Complex operator+ (Complex &c2);
private:
T a;
T b;
};
//构造函数的实现 写在了类的外部
template //每个模板函数前都要加这句
Complex::Complex(T a, T b)//类模板需要具体化
{
this->a = a;
this->b = b;
}
template
void Complex::printCom()
{
cout << "a:" << a << " b: " << b << endl;
}
//本质是 : 模板是两次 编译生成的 第一次生成的函数头 和第二次生成的函数头 不一样
//成员函数 实现 +运算符重载
template
Complex Complex::operator+ (Complex &c2)//类模板需要具体化
{
Complex tmp(a+c2.a, b+c2.b);//此处加不加都可以
return tmp;
}
//友元函数 实现 << 运算符重载
template
ostream & operator<<(ostream &out, Complex &c3)
{
out << c3.a << " + " << c3.b << "i" << endl;
return out;
}
//滥用 友元函数。结论:在不要用友元函数的地方不要用友元函数
template
Complex MySub(Complex &c1, Complex &c2)
{
Complex tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
void main()
{
//需要把模板类 进行具体化以后 才能定义对象 C++编译器要分配内存
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;
//滥用友元函数
{
Complex c4 = MySub(c1, c2);
cout << c4 << endl;
}
cout<<"hello..."<
六 类模板中的关键字static
/*
编译器并不是把函数模板处理成能够处理任意类的函数
编译器从函数模板通过具体类型产生不同的函数
编译器会对函数模板进行两次编译
在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。
*/
#include
using namespace std;
template
class AA
{
public:
static T m_a;
};
template
T AA::m_a = 0;
class AA1
{
public:
static int m_a;
};
int AA1::m_a = 0;
class AA2
{
public:
static char m_a;
};
char AA2::m_a = 0;
void main()
{
AA a1, a2, a3;
a1.m_a = 10;
a2.m_a ++;
a3.m_a ++;
cout << AA::m_a << endl;
AA b1, b2, b3;
b1.m_a = 'a';
b2.m_a ++;
b2.m_a ++ ;
cout << AA::m_a << endl;
//m_a 应该是 每一种类型的类 使用自己的m_a
cout<<"hello..."<