目录
一、类模板的作用
建立一个通用类,类中成员、数据类型可以不具体指定,用一个虚拟的类型来代表
二、类模板的语法
cpp
template<typename T>
//类
三、类模板的例子
cpp
//类模板
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 << "年龄:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
person<string,int> p1 ("悟空",99);
p1.showPerson();
}
四、类模板和函数模板的区别
①类模板没有自动类型推导的使用方法
②类模板在模板参数列表中可以有默认参数
cpp
//类模板与函数模板的区别
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 << "姓名:" << this->m_name << "年龄:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
//类模板没有自动类型推导的使用方式
person<string,int>p1("悟空",99);//可以编译
person p1 ("悟空",99);//不可以编译
p1.showPerson();
}
void test02()
{
//类模板在模板参数列表中可以有默认参数
person<string>p2("八戒",9);//在模板中定义了该属性默认是一个int形
p2.showPerson();
}
五、类模板中成员函数创建时机
在普通类中成员函数一开始就创建了;在类模板中成员函数在调用时才创建
cpp
class Person1
{
public:
void showPerson1()
{
cout << "Person1 show" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "Person2 show" << endl;
}
};
template<class T>
class Myclass
{
public:
T obj;
//类模板中的成员函数
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
}
当前函数代码段可以成功编译不报错,因为类模板中成员函数在调用时才创建所以编译时没调用不报错,obj并没有确定类型
cpp
void test01()
{
MyClass<Person1>m;
m.func1();
m.func2();
}
在上述代码段中添加该测试用例,编译器会报错,无法调用func2。
六、类模板对象做函数参数
类模板实例化出的对象向参数传参的方式一共有3种:
①指定传入的类型 -- 直接显示对象的数据类型 【最常用的方法】
②参数模板化 -- 将对象中的参数变为模板进行传递
③整个类模板化 -- 将这个对象类型 模板化进行传递
cpp
template<class T1,class T2>
class Person
{
public:
Person(T1 name,T2 age)
{
this.m_Name = name;
this.m_Age = age;
}
void showPerson
{
cout << "姓名:" << this.m_Name << endl;
cout << "年龄:" << this.m_Age << endl;
}
T1 m_Name;
T2 m_Age;
}
//指定传入的类型
void printPerson1(Person<string,int>&p)
{
p.showPerson();
}
void test01()
{
Person<string,int>p("悟空",800);
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("八戒",120);
printPerson2(p);
}
//整个类模板化
template<class T>
void printPerson3(T &p)
{
p.showPerson();
cout << "T数据类型:" << typeid(T).name << endl;
//会显示Person类型
}
void test03()
{
Person<string,int>p("唐僧",100);
printPerson3(p);
}
七、类模板与继承
注意事项:
①当子类继承的父类是一个类模板时,子类在声明的时候要指定出父类中T的类型
②如果不指定,编译器无法给子类分配内存
cpp
template<class T>
class Base
{
T m;
};
class Son :public Base //此方法是错误的,必须要知道父类中的T的数据类型才能继承给子类
{
};
class Son :public Base<int>//此方法正确
{
};
void test01()
{
T1 obj;
}
③如果想灵活指定出父类中T的类型,子类也需变为类模板
cpp
template<class T>
class Base
{
T m;
};
template<class T1,class T2>
class Son2 :public Base<T2>
{
public:
Son2()
{
cout << "T1类型:" << typeif(T1).name << endl;
cout << "T2类型:" << typeif(T2).name << endl;
}
T1 obj;
};
void test02()
{
Son2<int, char>S2;
}
八、类模板成员函数类外实现
cpp
//类内声明,类外实现
template<class T1,class T2>
class Person
{
public:
Person(T1 name,T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
//函数模板类外实现的构造函数
teemplate<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
this.m_Name = name;
this.m_Age = age;
}
//成员函数类外实现
teemplate<class T1,class T2>
void Person<T1,T2>::showPerson()
{
cout << "姓名:" << this.m_Name << endl;
cout << "年龄:" << this.m_Age << endl;
}
九、类模板分文件编写出现的常见问题与解决方法
问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决方法:
①直接包含.cpp源文件
②将声明和实现写到同一个文件中,并更改后缀名为.hpp【hpp仅是约定的名字,可改】
第一步在头文件中添加新建项
第二步添加头文件 -- Person.h
其中Person.h内容为:
cpp
#pragma once
#include <iostream>
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
Person.cpp内容为:
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>
Person<T1,T2>::showPerson()
{
cout << "姓名:" << this.m_Name << "年龄:" << this.m_Age << endl;
}
第四步在需要使用的文件中输入以下代码:
部分编译器会出现报错无法正常生成代码[因为类模板中的成员函数是在调用时创建,编译器无法正确识别该代码],解决方法有以下:
解决方案1:将改行代码修改为 #include "Person.cpp",让编译器将.h和.cpp两个文件都阅读一次后再做接下来的解析,但是不建议使用该方式,会使二进制包变大
cpp
#include <iostream>
using namespace std;
#include "Person.h"//解决方案1:将改行代码修改为 #include "Person.cpp",让编译器将.h和.cpp两个文件都阅读一次后再做接下来的解析,但是不建议使用该方式,会使二进制包变大
void test01()
{
Person<string,int>p("ming",18);
p.showPerson();
}
解决方案2:将.h文件中的内容和.cpp文件中的内容写一个.hpp文件中放在头文件中,声明和实现都在一个文件中【伪份文件编写】
cpp
#include <iostream>
using namespace std;
#include "Person.hpp"//解决方案2
void test01()
{
Person<string,int>p("ming",18);
p.showPerson();
}
十、类模板与友元
全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在
cpp
//通过全局函数打印Person信息
//提前让编译器知道Person类存在
template<class T1,class T2>
class Person;
//全局函数不用加作用域
template<class T1,class T2>
friend 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;
};
十一、类模板案例
实现以一个通用的数组类