目录
一、类模板的作用
二、类模板的语法
三、类模板的例子
四、类模板和函数模板的区别
五、类模板中成员函数创建时机
六、类模板对象做函数参数
七、类模板与继承
八、类模板成员函数类外实现
九、类模板分文件编写
十、类模板与友元
十一、类模板案例
一、类模板的作用
建立一个通用类,类中成员、数据类型可以不具体指定,用一个虚拟的类型来代表
二、类模板的语法
template<typename T>
//类
三、类模板的例子
//类模板
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();
}
四、类模板和函数模板的区别
①类模板没有自动类型推导的使用方法
②类模板在模板参数列表中可以有默认参数
//类模板与函数模板的区别
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();
}
五、类模板中成员函数创建时机
在普通类中成员函数一开始就创建了;在类模板中成员函数在调用时才创建
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并没有确定类型
void test01()
{MyClass<Person1>m;m.func1();m.func2();
}
在上述代码段中添加该测试用例,编译器会报错,无法调用func2。
六、类模板对象做函数参数
类模板实例化出的对象向参数传参的方式一共有3种:
①指定传入的类型 -- 直接显示对象的数据类型 【最常用的方法】
②参数模板化 -- 将对象中的参数变为模板进行传递
③整个类模板化 -- 将这个对象类型 模板化进行传递
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的类型
②如果不指定,编译器无法给子类分配内存
template<class T>
class Base
{T m;
};class Son :public Base //此方法是错误的,必须要知道父类中的T的数据类型才能继承给子类
{};class Son :public Base<int>//此方法正确
{};void test01()
{T1 obj;
}
③如果想灵活指定出父类中T的类型,子类也需变为类模板
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;
}
八、类模板成员函数类外实现
//类内声明,类外实现
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内容为:
#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内容为:
#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两个文件都阅读一次后再做接下来的解析,但是不建议使用该方式,会使二进制包变大
#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文件中放在头文件中,声明和实现都在一个文件中【伪份文件编写】
相关内容可参考文章https://blog.csdn.net/HXG2006/article/details/128754137?ops_request_misc=&request_id=&biz_id=102&utm_term=.hpp%E5%92%8C.cpp%E5%8C%BA%E5%88%AB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-128754137.142^v100^pc_search_result_base4&spm=1018.2226.3001.4187
#include <iostream>
using namespace std;
#include "Person.hpp"//解决方案2
void test01()
{Person<string,int>p("ming",18);p.showPerson();
}
十、类模板与友元
全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在
//通过全局函数打印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;
};
十一、类模板案例
实现以一个通用的数组类