模版就是建立一个通用的模具,大大提高复用性。
1、函数模板
函数模板的作用:建立一个通用的函数,其函数返回值类型和形参列表可以不具体确定,用一个虚拟的类型来代表。
目的:将类型参数化。
语法:
template<typename T> //函数的声明或定义
template:声明创建模板
typename:表明其后面的是一种数据类型,可以用class替代
T:通用的数据类型,名称可以替换,通常为大写字母
普通函数调用时可以发生自动类型转换(隐式类型转换)。
模板并不是万能的。
例一:交换两个数据,根据类型分别写对应函数。
#include<iostream>
using namespace std;
//交换两个整形数据
void swapInt(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
//交换两个浮点型数据
void swapDouble(double& a, double& b)
{
double temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
swapInt(a, b);
cout << a << endl;
cout << b << endl;
double c = 1.1;
double d = 2.2;
swapDouble(c,d);
cout << c << endl;
cout << d << endl;
}
int main()
{
test01();
}
例二:使用函数模板完成任意类型数据的相加
#include<iostream>
using namespace std;
//交换两个T型数据
template<typename T>
void Myswap(T& a, T&b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
//1、自动类型推导
Myswap(a, b);
//2、显示指定类型
//Myswap<int>(a,b);
cout << a << endl;
cout << b << endl;
}
int main()
{
test01();
}
注意:模板必须确定T的数据类型,才可以使用。且数据类型必须一致。例如上例中将整形和字符型进行交换,数据类型不同将会报错。
例:利用函数模板对不同数据类型的数组进行从大到小的选择排序。
#include<iostream>
using namespace std;
//交换两个T型数据
template<typename T>
void mySwap(T&a,T&b)
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void printArr(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
template<typename T>
void Mysort(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
int max = i;
for (int j = i + 1; j < len; j++)
{
if (arr[max] < arr[j])
{
max = j;
}
}
if (max != i)
{
mySwap(arr[max], arr[i]);
}
}
}
void test01()
{
char charArr[] = "badcfe"; //测试char数组
int num = sizeof(charArr) / sizeof(char);
Mysort(charArr, num);
printArr(charArr, num);
}
int main()
{
test01();
}
2、类模板
作用:建立一个通用的类,类中的成员函数和数据类型可以不具体指定,用一个虚拟的类型代表。
语法:
Template<typename T>
类
template<typename T> //声明创建模板
template:声明创建模板
typename:表明其后面的是一种数据类型,可以用class替代
T:通用的数据类型,名称可以替换,通常为大写字母
类模板的传入参数也需要在确定对象时也需要初始化。
#include<iostream>
#include"string"
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 << "Name: " << this->m_Name << endl;
cout << "Age: " << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
Person<string, int> p1("wxq", 29);
//<>模版的参数列表
p1.showPerson();
}
int main()
{
test01();
}
2.1、类模板和函数模板的区别
1、类模板没有自动类型推导的使用方式
#include<iostream>
#include"string"
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 << "Name: " << this->m_Name << endl;
cout << "Age: " << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
//类模板没有自动推导类型
void test01()
{
//Person p1("wxq", 29);
//错误,无法用自动类型推导
Person<string, int> p2("wxq", 29);
//只能用显示指定类型
p2.showPerson();
}
int main()
{
test01();
}
2、类模板在模板参数列表中可以有默认参数
#include<iostream>
#include"string"
using namespace std;
template<class NameType, class AgeType=int>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "Name: " << this->m_Name << endl;
cout << "Age: " << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
//类模板在模板参数列表中可以有默认参数
void test01()
{
Person<string> p2("wxq", 29);
//只能用显示指定类型
p2.showPerson();
}
int main()
{
test01();
}
类模板中的成员函数
类模板中的成员函数和普通类中的成员函数的创建时机是不同的:普通类的成员函数一开始就可以创建成员函数;类模板中的成员函数在调用时创建。
#include<iostream>
#include"string"
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout << "Person1 shows!" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "Person2 shows!" << endl;
}
};
template<class T>
class Person
{
public:
T obj;
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
};
void test01()
{
Person<Person1> m;
m.func1();
//m.func2(); //报错,因为m中无showPerson2函数
}
int main()
{
test01();
}
类模板对象做函数参数
类模板实例出的对象作为函数的实参,一共有三种传入方式:
1、指定类型的传入:直接显示对象的数据类型(主要使用)
2、参数模板化:将对象中的参数变为模板进行传递(类模板配合函数模版)
3、整个类模板化:将这个对象类型模板化进行传递
#include<iostream>
#include"string"
using namespace std;
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "name is " <<m_Name<< endl;
cout << "age is " << m_Age<<endl;
}
T1 m_Name;
T2 m_Age;
};
//1、指定传入类型
void printPerson1(Person<string,int>& p)
{
p.showPerson();
}
void test01()
{
Person<string, int> p("Wang", 100);
printPerson1(p);
}
//2、将参数模板化
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
p.showPerson();
}
void test02()
{
Person<string, int> p("Li", 90);
printPerson2(p);
}
//3、整个类模板化
template<class T>
void printPerson3(T& p)
{
p.showPerson();
}
void test03()
{
Person<string, int> p("Zhang", 80);
printPerson3(p);
}
int main()
{
test01();
test02();
test03();
}
类模板与继承
需要注意几个点:
1、当子类继承的父类是一个类模板时,子类在声明时需要指定父类中T的类型;如果不指定,编译器则无法给子类分配内存。
2、如果想灵活指定父类中的T的类型,子类也需要变为类模板。
如果父类是类模板,子类在继承时需要指定父类中的数据类型。
#include<iostream>
#include"string"
using namespace std;
template<class T>
class Base
{
T m;
};
//class Son :public Base //错误,必须知道父类的T的数据类型,才能继承给子类
//class Son :public Base<int> //正确,但是子类只能为int
template<class T1, class T2>
class Son :public Base<T2>
{
T1 obj;
};
void test01()
{
Son<int,char> s1; //int传给T1,char传给T2,父类中的T也为char
}
int main()
{
test01();
}
类模板成员函数的类外实现
#include<iostream>
#include"string"
using namespace std;
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
void show();
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>::show()
{
cout << "Name is " << this->m_Name << endl;
cout << "Age is " << this->m_Age << endl;
}
void test01()
{
Person<string, int> p("Wang", 100);
p.show();
}
int main()
{
test01();
}
类模板分文件编写
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。
方法:
1、直接包含CPP源文件
2、将声明个实现写在同一个文件中,并更改后缀名为.hpp。hpp是一个约定的概念,表示的就是类模板的分文件编写。(用的多)
方法1:
Person.h | Person.cpp | Main.cpp |
#pragma once #include<iostream> #include"string" using namespace std; template<class T1, class T2> class Person { public: Person(T1 name, T2 age); void show(); T1 m_Name; T2 m_Age; }; | #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>::show() { cout << "Name is " << this->m_Name << endl; cout << "Age is " << this->m_Age << endl; } | #include"Person.cpp" //包含的是cpp,不是h //第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件 void test01() { Person<string, int> p("Wang", 100); p.show(); } int main() { test01(); } |
方法2
Person.hpp | Main.cpp |
#pragma once #include<iostream> #include"string" using namespace std; template<class T1, class T2> class Person { public: Person(T1 name, T2 age); void show(); 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>::show() { cout << "Name is " << this->m_Name << endl; cout << "Age is " << this->m_Age << endl; } | #include"Person.hpp" //包含的是cpp,不是h //第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件 void test01() { Person<string, int> p("Wang", 100); p.show(); } int main() { test01(); } |
类模板与友元
主要包括友元函数的类内实现和类外实现。类内直接声明友元即可,类外实现则需要提前让编译器指导全局函数的存在。
case 1:友元函数在类内实现
#include<iostream>
#include"string"
using namespace std;
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
friend void show(Person<T1, T2> p)
{
cout << "Name is " <<p.m_Name << endl;
cout << "Age is " << p.m_Age << endl;
}
private:
T1 m_Name;
T2 m_Age;
};
//1、全局函数在类内实现
void test01()
{
Person<string, int> p("Wang", 100);
show(p);
}
int main()
{
test01();
}
case 2:友元函数在类外实现
#include<iostream>
#include"string"
using namespace std;
//提前让编译器知道person类的存在
template<class T1, class T2>
class Person;
//2、全局函数类外实现,先让编译器知道有个类外函数的存在
template<class T1, class T2>
void show(Person<T1, T2> p)
{
cout << "Name is " << p.m_Name << endl;
cout << "Age is " << p.m_Age << endl;
}
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
//加空模板的参数列表 <>
//如果全局函数是类外实现,需要让编译器提前知道这个函数的存在
friend void show<>(Person<T1, T2> p);
private:
T1 m_Name;
T2 m_Age;
};
void test01()
{
Person<string, int> p("Wang", 100);
show(p);
}
int main()
{
test01();
}
类外实现比较复杂,首先需要让编译器知道有一个全局函数的存在,由于用到了person,要先让编译器知道person类存在,person类又是一个类模板,因此还得声明类模板。
如无特殊需求,尽量类内实现。