目录
1.1函数模板语法
1.2函数模板的使用方式
1.2.1自动类型推导
1.2.2显示指定类型
1.3普通函数与模板函数
1.3.1区别
1.3.2调用规则
1.4模板的局限性
1.4.1模板的具体化
1.5类模板
1.5.1基本语法
1.5.2类模板对象做函数参数
1.5.3类模板与继承
1.5.4类模板成员函数类外实现
1.5.5类模板分文件编写
1.5.6类模板与友元
1.5.6.1类内实现
1.5.6.2类外实现
1.5.7举例:通过类模板创建数组类MyArray
1.1函数模板语法
建立通用的模具,大大提高复用性,模板只是一个框架,不可以直接使用
//T代表数据类型
template<typename T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
1.2函数模板的使用方式
1.2.1自动类型推导
int a = 10;
int b = 20;
mySwap(a, b);
编译器会自动推导出T的类型为int
1.2.2显示指定类型
int a = 10;
int b = 20;
mySwap<int>(a, b);
明确指定T的类型为int
注意事项:
①自动类型推导,必须正确推出T的类型,否则无法成功调用函数
②模版必须指定或推导出T的类型,否则不可单独使用
1.3普通函数与模板函数
1.3.1区别
- 普通函数调用时,可以发生自动类型转换(隐式类型转换);
- 函数模板调用时,若利用自动类型推导,不会发生隐式类型转换;若利用显示指定类型,可以发生隐式类型转换。
1.3.2调用规则
- 函数模板和普通函数都可以实现时,优先使用普通函数
- 可以通过空模板参数列表强制调用函数模板
- 函数模板可以重载
- 若函数模板可以产生更好的匹配,优先调用函数模板
注意:通过函数模板产生的函数,称为模板函数
myPrint(c1,c2)会调用函数模板,因为若调用普通函数,会进行类型转换,而调用函数模板,直接就能获取到数据类型,代价更小,因此调用函数模板
1.4模板的局限性
解决办法:
- 运算符重载。重载运算符>
- 为特定的类型(Person)提供具体化的模板
1.4.1模板的具体化
普通模板:
template<class T>
bool myCompare(T& a, T& b)
{
if (....)
...
}
具体化语法:以template<>开头,通过名称指出类型(提供对比功能的函数模板)
template<>bool myCompare(Person& a, Person& b)
{
if (....)
...
}
注意:若参数类型符合模板具体化的参数类型时,模板的具体化调用优先于普通模板的调用
1.5类模板
1.5.1基本语法
//typename也可以用class替换
template<typename T>
class ...
{
...
};
建立一个通用类,类中成员的数据类型不固定
注意事项:
1.模板参数列表可以有默认参数;
2.类模板没有自动推导;
3.类模板中的成员函数在被调用的时候才被创建
1.5.2类模板对象做函数参数
类模板如下:
1. 指定传入类型。即形参的模板参数列表明确数据类型
void doWork1(Person<string,int>& p);
2.参数模板化。即形参的模板参数列表也变为通用类型
注意:typeid(T).name() 查看T的数据类型
3.整个类模板化。即形参中对象的类型变为模板
1.5.3类模板与继承
①父类为类模板时,子类在继承时必须指定父类中T的类型,否在无法给子类分配内存
②父子类均为类模板时,在子类继承时,将模板参数列表改为T
1.5.4类模板成员函数类外实现
语法:
template<class T>
返回值类型 类名<T>::成员函数名(形参列表)
1.5.5类模板分文件编写
分文件编写会产生错误,因为类模板成员函数是在被调用时才创建的,分文件编写后,只能访问头文件,访问不到源文件的内容
解决办法:
- 将源文件包含进来。此做法不安全
- 将源文件和头文件结合到一个文件中,即hpp中
1.5.6类模板与友元
1.5.6.1类内实现
1.5.6.2类外实现
在类中声明友元时,需将printPerson作为函数模板声明,语法如下:
friend void printPerson<>(Person<T1,T2>& p);
注意:
- 若函数实现写在类之前,需要提前声明类之后使用
- 若函数实现写在类之后,需要提前声明函数和类之后使用
1.5.7举例:通过类模板创建数组类MyArray
template<class T>
class MyArray
{
public:
//有参构造
MyArray(int capacity)
{
m_Capacity = capacity;
m_Size = 0;
pAddress = new T[m_Capacity];
}
//拷贝构造
MyArray(const MyArray& arr)
{
m_Capacity = arr.m_Capacity;
m_Size = arr.m_Size;
if (nullptr != arr.pAddress)
{
pAddress = new T[m_Capacity];
strcpy(arr.pAddress, pAddress, m_Size);
}
}
//析构函数
~MyArray()
{
if (nullptr != pAddress)
{
delete[] pAddress;
pAddress = nullptr;
}
}
//operator=
MyArray& operator=(const MyArray& arr)
{
if (nullptr != pAddress)
{
delete[] pAddress;
pAddress = nullptr;
}
m_Capacity = arr.m_Capacity;
m_Size = arr.m_Size;
pAddress = new T[m_Capacity];
strcpy(arr.pAddress, pAddress, m_Size);
return *this;
}
//operator[]
T& operator[](int pos)
{
return pAddress->[pos];
}
//尾插
void pushBack(T val)
{
if (m_Capacity == m_Size)
{
return;
}
pAddress[m_Size] = val;
m_Size++;
}
//删掉末尾元素
void popBack()
{
if (m_Size == 0 || m_Capacity == 0)
{
return;
}
m_Size--;
}
//获取容量
int getCapacity()
{
return m_Capacity;
}
//获取当前大小
int getSize()
{
return m_Size;
}
private:
int m_Capacity;
int m_Size;
T* pAddress;
};