20. 类模板
一、什么是类模板
类模板用于建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟的类型来代替。它的语法格式如下:
template<typename T>
类模板与函数模板相比主要有两点区别:1) 类模板没有自动类型推导的方式。2) 类模板在模板参数中可以有默认参数。
#include <iostream>
using namespace std;
// 类模板中可以使用默认参数
template<class NameType, typename AgeType=int>
class Person
{
public:
NameType name;
AgeType age;
Person(void) {}
Person(NameType name, AgeType age) : name(name), age(age) {}
void showPerson(void)
{
cout << "{name: " << name << ", age: " << age << "}" << endl;
}
};
int main(void)
{
// 指定NameType为string,AgeType默认为int
Person<string> p1("Sakura", 10);
p1.showPerson();
return 0;
}
类模板中的成员函数并不是一开始就创建的,而是在模板调用时再生成的。
二、类模板对象做函数参数
类模板对象也可以作为函数的参数,总共有三种方式传入:
- 指定传入的类型 —— 直接显示对象的数据类型。
- 参数模板化 —— 将对象中的参数变为模板进行传递。
- 整个类模板化 —— 将这个对象类型模板化进行传递。
#include <iostream>
using namespace std;
// 类模板中可以使用默认参数
template<class NameType, typename AgeType=int>
class Person
{
public:
NameType name;
AgeType age;
Person(void) {}
Person(NameType name, AgeType age) : name(name), age(age) {}
void showPerson(void)
{
cout << "{name: " << name << ", age: " << age << "}" << endl;
}
};
// 1、指定传入类型
void printPerson1(Person<string, int> &p)
{
p.showPerson();
}
// 2、参数模板化
template <typename NameType, typename AgeType>
void printPerson2(Person<NameType, AgeType> &p)
{
p.showPerson();
}
// 3、这个类模板化
template<typename T>
void printPerson3(T &p)
{
p.showPerson();
}
int main(void)
{
Person<string, int> p("Sakura", 10);
printPerson1(p);
printPerson2(p);
printPerson3(p);
return 0;
}
三、类模板与继承
当子类继承的父类是一个类模板时,子类在声明的时候,要指出父类中 T 的类型。如果不指定,编译器无法给子类分配内存。如果想要灵活指定出父类父类中的 T 的类型,子类也需要变成类模板。
#include <iostream>
using namespace std;
template<typename T>
class SuperClass
{
public:
T a;
};
// 子类继承模板类,必须知道父类中T类型
class SubClass1 : public SuperClass<string>
{
};
// 如果想要灵活指定父类中T类型,则需要使用模板类
template<typename T1, typename T2>
class SubClass2: public SuperClass<T1>
{
T2 b;
};
int main(void)
{
SubClass1 subClass1;
SubClass2<string, int> subClass2;
return 0;
}
四、类模板成员函数类外实现
类模板中成员函数的类外实现时,需要加上模板参数列表。
新建一个 person.hpp 文件用来保存类的声明和方法。
#pragma once
#include <iostream>
using namespace std;
// 类模板中可以使用默认参数
template<class NameType, typename AgeType=int>
class Person
{
public:
NameType name;
AgeType age;
Person(void);
Person(NameType name, AgeType age);
void showPerson(void);
};
在包含 main() 函数的文件中包含刚才定义的头文件,然后使用。
#include <iostream>
// 这里不要包含头文件要包含源文件
// #include "person.hpp"
using namespace std;
int main(void)
{
Person<string> p1("Sakura", 10);
p1.showPerson();
return 0;
}
五、类模板与友元
修改 person.hpp 文件中内容。
#pragma once
#include <iostream>
#include <cstring>
using namespace std;
template<typename NameType, typename AgeType>
class Person;
template<typename NameType, typename AgeType>
void showPerson(Person<NameType, AgeType> p);
// 类模板中可以使用默认参数
template<class NameType, typename AgeType=int>
class Person
{
// 加空模板的参数列表
/// 如果全局函数是类外实现的,需要让编译器提前知道这个函数的存在
friend void showPerson<>(Person<NameType, AgeType> p);
private:
NameType name;
AgeType age;
public:
Person(void);
Person(NameType name, AgeType age);
};
// 类模板的构造函数类外实现
template<typename NameType, typename AgeType>
Person<NameType, AgeType>::Person(void) {}
template<typename NameType, typename AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
// 全局函数做友元类外实现
template<typename NameType, typename AgeType>
void showPerson(Person<NameType, AgeType> p)
{
cout << "{name: " << p.name << ", age: " << p.age << "}" << endl;
}
修改包含 main() 函数的文件中的内容。
#include <iostream>
#include "person.hpp"
using namespace std;
int main(void)
{
Person<string> p1("Sakura", 10);
showPerson(p1);
return 0;
}