C++泛型编程使用模板技术来实现。
一、函数模板基本使用
函数模板就是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表,这个通用函数就称为函数模板。
template<class T>等价于template<typename T>
#include<iostream>
using namespace std;
//使用函数模板实现不同类型的数据交换
//类型参数化 泛型编程 -- 模板技术
template<class T> //告诉编译器出现T时不要报错,T是一个通用类型
void mySwap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
void test() {
int a = 10;
int b = 20;
//1.自动推导类型,必须有参数类型才能推导
mySwap(a, b);
//2.显式指定类型
mySwap<int>(a, b);
//3.模板必须要指定T才能使用
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main() {
test();
system("pause");
return 0;
}
二、实现通用的数组排序
使用函数模板实现对char和int类型数组进行排序。
//使用函数模板实现对char和int类型数组进行排序
//排序规则是从小到大,利用快速排序
#include <iostream>
#include <string>
using namespace std;
//先把元素自己排好位置,然后给左右数组排序
template<class T>
void mySort(T arr[], int low, int high) {
if (low >= high) return;
int i = low, j = high;
int key = arr[low];
while (i < j) {
while (arr[j] >= key && i < j) j--;
arr[i] = arr[j];
while (arr[j] <= key && i < j) i++;
arr[j] = arr[i];
}
arr[i] = key;
mySort(arr, low, i - 1);
mySort(arr, i + 1, high);
return;
}
//打印数组
template<class T>
void printArray(T arr[], int len) {
for (int i = 0; i < len; ++i) {
cout << arr[i] << " ";
}
cout << endl;
return;
}
//char类型测试
void test01() {
char charArr[] = "helloworld";
int len = sizeof(charArr) / sizeof(char);
mySort(charArr, 0, len-1);
printArray(charArr, len);
}
//int类型测试
void test02() {
int intArr[] = { 4, 1, 7, 100, 35 };
int len2 = sizeof(intArr) / sizeof(int);
mySort(intArr, 0, len2 - 1);
printArray(intArr, len2);
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
三、函数模板与普通模板的区别及调用规则
函数模板和类模板的上面都要写template<class T>,后面必须要紧跟着函数或类,跟着的是函数,就是函数模板,跟着的是类,就是类模板。
1.普通函数与函数模板的区别
普通的函数可以进行隐式类型转换,函数模板不可以进行隐式类型转换。
2.普通函数和模板函数的调用规则
(1)如果出现重载,优先使用普通函数调用,如果普通函数只有声明没有实现,会报错。
(2)如果想强制调用模板,可以使用空参数列表,即myPrint<>(a, b)
(3)函数模板也可以发生重载(参数数量不同)。
(4)如果函数模板可以产生更好的匹配,那么优先调用函数模板。
四、模板机制剖析
(1)编译器并不是把函数模板处理成能处理任何类型的函数;
(2)函数模板通过具体类型产生不同的函数,通过模板生成的函数叫模板函数;
(3)编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
五、函数模板的局限性及解决
如果函数中有a==b的判断,但是此时a和b都是结构体,就无法实现。
解决办法:前面有基础函数,后面通过第三代具体化自定义数据类型来解决上述问题,如果具体化能够优先匹配,那么就选择具体化。
语法:template<> 返回值 函数名 <具体类型> (参数)
template<> bool myCompare<Person> (Person &a, Person& b) {
if(a.m_Age == b.m_Age) {
return true;
}
return false;
}
六、类模板的基本使用
//类模板
template <class NameType, class AgeType>
class Person{
public:
Person(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
NameType m_name;
AgeType m_Age;
}
(1)类模板不支持自动类型推导,Person p("张三", 100)会报错,只能使用显式指定类型,
Person<string, int> p("张三", 100)。
(2)类模板可以有默认,template <class NameType, class AgeType = int>
七、成员函数创建时机
成员函数一开始不会创建,而是在运行时才会创建。
八、类模板做函数的参数
类模板做函数参数有以下几种方式:
- 指定传入类型
- 参数模板化
- 整体类型化
#include <string>
#include <iostream>
using namespace std;
template <class NameType, class AgeType>
class Person {
public:
Person(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "姓名:" << this->m_Name << endl;
cout << "年龄:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
//类模板做函数的参数
//方式1 -- 指定传入类型
void doWork(Person<string, int>& p) {
p.showPerson();
}
void test01() {
Person<string, int> p("YD", 20);
doWork(p);
}
//方式2 -- 参数模板化
template<class T1, class T2>
void doWork2(Person<T1, T2>& p) {
//如何查看类型
cout << typeid(T1).name() << endl;
p.showPerson();
}
void test02() {
Person<string, int> p("YD", 20);
doWork2(p);
}
//方式3 -- 整体模板化
template<class T>
void doWork3(T& p) {
p.showPerson();
}
void test03() {
Person<string, int> p("YD", 20);
doWork3(p);
}
int main()
{
//test01();
//test02();
test03();
system("pause");
return 0;
}
九、类模板碰到继承的问题以及解决
(1)在继承时如果基类是模板类,必须指定基类中T的类型,否则无法给T分配内存。
template <class T>
class Base {
public:
T m_A;
};
//在继承时必须指定基类中T的类型,否则无法给T分配内存
class Child : public Base<int> {
};
(2) 可以由用户指定基类类型。
template <class T>
class Base {
public:
T m_A; //double类型
};
//Child2也是模板
template<class T1, class T2>
class Child2 : public Base<T2>{
public:
T1 m_B; //int类型
};
十、类模板的类外实现成员函数
//类外实现成员函数
template<class T1, class T2>
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名:" << this->m_Name << endl;
cout << "年龄:" << this->m_Age << endl;
}
十一、类模板的分文件编写问题以及解决
以下代码会出现无法解析的外部命令错误,因为C++中进行的是单元编译,导致.h头文件不会创建函数的实现,无法解析外部命令。
解决办法是将test.cpp中头文件 #include "Person.h" 替换为 #include "Person.cpp"(不推荐)。
建议模板不要做分文件编写,写在一个文件中进行声明和实现,最后把后缀名改为.hpp。
//Person.h文件
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
//Person.cpp文件
#include "Person.h"
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名:" << this->m_Name << endl;
cout << "年龄:" << this->m_Age << endl;
}
//test.cpp
#include <iostream>
#include <string>
#include "Person.h"
using namespace std;
int main() {
Person<string, int> p("XX", 10);
p.showPerson();
system("pause");
return 0;
}
十二、友元函数遇到类模板
(1)友元函数在类内实现
#include <string>
#include <iostream>
using namespace std;
//友元函数是为了保护私有成员属性
template<class T1, class T2>
class Person {
//友元函数在类内实现
friend void printPerson(Person<T1, T2>& p) {
cout << "姓名:" << p.m_Name << endl;
cout << "年龄:" << p.m_Age << endl;
return;
}
public:
Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
void test01() {
Person<string, int> p("TOM", 10);
printPerson(p);
}
int main()
{
test01();
system("pause");
return 0;
}
(2)友元函数在类外实现
#include <string>
#include <iostream>
using namespace std;
//让编译器提前看到printPerson的声明
//让编译器看到Person的声明
template<class T1, class T2> class Person;
template<class T1, class T2> void printPerson(Person<T1, T2>& p);
//友元函数是为了保护私有成员属性
template<class T1, class T2>
class Person {
//友元函数在类内实现 利用空参数列表告诉编译器这是模板函数的声明
friend void printPerson<>(Person<T1, T2>& p); //上面没模板,是普通函数
public:
Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
template<class T1, class T2>
void printPerson(Person<T1, T2>& p) {
cout << "姓名:" << p.m_Name << endl;
cout << "年龄:" << p.m_Age << endl;
return;
}
void test01() {
Person<string, int> p("TOM", 10);
printPerson(p);
}
int main()
{
test01();
system("pause");
return 0;
}