模板概述
类型以参数的形式指定,这样的函数或类称为函数模板或类模板。
模板的特点:
- 模板不可以直接使用,它只是一个框架
- 模板的通用并不是万能的
函数模板
函数模板语法
语法:
1 2
| template<typename T> 函数声明或定义
|
解释:
template
:声明创建模板
typename
:表面其后面的符号是一种数据类型,可以用
class
代替
T
:通用的数据类型,名称可以替换,通常为大写字母。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream>
using namespace std;
template<typename T> void mySwap(T &a, T &b) { T temp = a; a = b; b = temp; }
int main() {
int a = 10; int b = 20;
mySwap(a, b); cout << "a = " << a << endl; cout << "b = " << b << endl;
mySwap<int>(a, b); cout << "a = " << a << endl; cout << "b = " << b << endl;
return 0; }
|
- 使用函数模板有两种方式:自动类型推导、显示指定类类型。
普通函数与函数模板的区别
- 普通函数调用时可以发生自动类型转换
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream>
using namespace std;
int myAdd01(int a, int b) { return a + b; }
template<class T> T myAdd02(T a, T b) { return a + b; }
int main() {
int a = 10; int b = 20; char c = 'c';
cout << myAdd01(a, c) << endl;
myAdd02<int>(a, c);
return 0; }
|
普通函数与函数模板的调用规则
调用规则如下:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <iostream>
using namespace std;
void myPrint(int a, int b) { cout << "调用的普通函数" << endl; }
template<typename T> void myPrint(T a, T b) { cout << "调用的模板" << endl; }
template<typename T> void myPrint(T a, T b, T c) { cout << "调用重载的模板" << endl; }
int main() {
int a = 10; int b = 20; myPrint(a, b);
myPrint<>(a, b);
int c = 30; myPrint(a, b, c);
char c1 = 'a'; char c2 = 'b'; myPrint(c1, c2);
return 0; }
|
输出结果:
输出结果
既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
模板具体化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <iostream>
using namespace std;
class Person { public: Person(string name, int age) { this->mName = name; this->mAge = age; } string mName; int mAge; };
template<class T> void mySwap(T &a, T &b) { T temp = a; a = b; b = temp; }
template<> void mySwap<Person>(Person &p1, Person &p2) { string nameTemp; int ageTemp; nameTemp = p1.mName; p1.mName = p2.mName; p2.mName = nameTemp;
ageTemp = p1.mAge; p1.mAge = p2.mAge; p2.mAge = ageTemp; }
int main() { Person P1("Tom", 10); Person P2("Jerry", 20);
cout << "P1 Name = " << P1.mName << " P1 Age = " << P1.mAge << endl; cout << "P2 Name = " << P2.mName << " P2 Age = " << P2.mAge << endl; mySwap(P1, P2); cout << "P1 Name = " << P1.mName << " P1 Age = " << P1.mAge << endl; cout << "P2 Name = " << P2.mName << " P2 Age = " << P2.mAge << endl; return 0; }
|
输出结果:
输出结果
类模板
类模板语法
语法:
解释:
template
:声明创建模板
typename
:表明其后面的符号是一种数据类型,可以用
class
代替
T
:通用的数据类型,名称可以替换,通常为大写字母。
类模板与函数模板区别
类模板与函数模板区别主要有两点:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
类模板对象做函数参数
一共有三种传入方式:
- 指定传入的类型:直接显示对象的数据类型
- 参数模板化:将对象中的参数变为模板进行传递
- 整个类模板化:将这个对象类型 模板化进行传递
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <iostream> #include <string>
using namespace std;
template<class NameType, class AgeType = int> class Person { public: Person(NameType name, AgeType age) { this->mName = name; this->mAge = age; } void showPerson() { cout << "name: " << this->mName << " age: " << this->mAge << endl; }
public: NameType mName; AgeType mAge; };
void printPerson1(Person<string, int> &p) { p.showPerson(); } void test01() { Person<string, int> p("孙悟空", 100); printPerson1(p); }
template<class T1, class T2> void printPerson2(Person<T1, T2> &p) { p.showPerson(); cout << "T1的类型为: " << typeid(T1).name() << endl; cout << "T2的类型为: " << typeid(T2).name() << endl; } void test02() { Person<string, int> p("猪八戒", 90); printPerson2(p); }
template<class T> void printPerson3(T &p) { cout << "T的类型为: " << typeid(T).name() << endl; p.showPerson(); } void test03() { Person<string, int> p("唐僧", 30); printPerson3(p); }
int main() {
test01(); test02(); test03();
return 0; }
|
输出结果:
输出结果
类模板与继承
当类模板碰到继承时,需要注意一下几点:
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中 T
的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中 T 的类型,子类也需变为类模板。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <iostream> using namespace std;
template<class T> class Base { T m; };
class Son : public Base<int> {}; void test01() { Son c; }
template<class T1, class T2> class Son2 : public Base<T2> { public: Son2() { cout << typeid(T1).name() << endl; cout << typeid(T2).name() << endl; } };
void test02() { Son2<int, char> child1; }
int main() {
test01(); test02();
return 0; }
|
类模板成员函数类外实现
类模板中成员函数类外实现时,需要加上模板参数列表。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <iostream> using namespace std;
#include <string>
template<class T1, class T2> class Person { public: Person(T1 name, T2 age); void showPerson();
public: 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 << " 年龄:" << this->m_Age << endl; }
int main() {
Person<string, int> p("Tom", 20); p.showPerson();
return 0; }
|
输出结果:
输出结果
类模板分文件编写
问题:
- 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
- 解决方式 1:直接包含
.cpp
源文件
- 解决方式 2:将声明和实现写到同一个文件中,并更改后缀名为
.hpp
,hpp
是约定的名称,并不是强制。
类模板与友元
全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在
建议全局函数做类内实现,用法简单,而且编译器可以直接识别。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <iostream> #include <string>
using namespace std;
template<class T1, class T2> class Person;
template<class T1, class T2> void printPerson2(Person<T1, T2> &p) { cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl; }
template<class T1, class T2> class Person { friend void printPerson(Person<T1, T2> &p) { cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl; }
friend void printPerson2<>(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; };
void test01() { Person<string, int> p("Tom", 20); printPerson(p); }
void test02() { Person<string, int> p("Jerry", 30); printPerson2(p); }
int main() {
test02();
return 0; }
|