前言
什么是模板?
- 模板是一个泛型编程的概念,即不考虑类型的一种编程方式,能够实现代码重用,提高效率
- 模板可分为函数模板、类模板
模板的声明和定义
模板的声明有两种,一种就是typename,另外一种就是使用class ,一般使用一种声明格式就可以了,不建议混合使用。
- template <typename T>
- template <class T>
template就是模板的意思,声明创建模板typename
表明其后面的符号是一种数据类型,可以用class
代替T
是通用的数据类型,名称可以替换,通常为大写字母
函数模板
模板函数:建立一个通用的函数,其返回值类型,形参的类型都不确定指定,而是采用虚拟类型来代替,对于功能相同的函数,就无须重复写代码,并且模板函数和普通函数长相及使用方法非常类似,唯一的区别,类型参数化。
语法:
template<typename 类型参数1, typename 类型参数2, ...>
返回值类型名 函数名(参数表)
{
//函数体的定义
}
//或者使用下面这种方式
template<class 类型参数1, class类型参数2, ...>
返回值类型名 函数名(参数表)
{
//函数体的定义
}
例如交换两个数的函数模板:
template<typename T>
T Swap(T a, T b){
temp = a;
a = b;
b = temp;
}
int main(){
//当输入数据是整型
int a = 19;
int b = 20;
Swap(a,b);
cout <<"交换后的结果是:" << endl;
cout <<"a = " << a << endl;
cout <<"b = " << b << endl;
//当输入数据是浮点型
double a = 19.1;
double b = 20.5;
Swap(a,b);
cout <<"交换后的结果是:" << endl;
cout <<"a = " << a << endl;
cout <<"b = " << b << endl;
}
如果不用模板的话,针对不同变量的数据类型就需要分别写两个交换函数, 但是用了模板,只需一个函数就可以对所用类型的数据类型进行交换。
注意:使用模板函数时并不是所有的变量类型都适用,有时候会遇到一些特殊的类型需要特殊处理,不能直接使用当前的模板函数,所以此时我们就需要对该类型特化出一个模板函数(就是写出一个模板函数专门给该类型使用
在写特例化的模板函数时,需要直接在形参参数列表中写明参数类型,不能再使用类型参数。
特例化分为部分特例化和全部特例化。全部特例化就是上边这种,将两个参数都写明参数类型。部分特例化就是只将一部分的参数写明参数类型
template<>//全特化,此处为空
char Swap(const char a, const char b)
{
temp = a;
a = b;
b = temp;
}
类模板
使用template关键字不但可以定义函数模板,也可以定义类模板,类模板代表一族类,是用来描述通用数据或处理方法的机制,它使类中的一些数据成员和成员函数的参数或返回值可以取任意的数据类型。类模板可以说是用类生成类,减少了类的定义数量。
//类模版定义
template <类型形式及参数>
class 类模板名{
//类成员声明
}
//在类外面定义成员函数
template <类型形式及参数>
类型名 类名<模板参数标识符列表>::函数名(参数表)
//类模板成员类外实现
template<typename T1, typename T2>
class Person
{
public:
Person(T1 name, T2 age);
/*{
this->m_Name = name;
this->m_Age = age;
}*/
void showPerson();
/*{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}*/
T1 m_Name;
T2 m_Age;
};
//构造函数的类外实现
template<typename T1,typename T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
//成员函数的类外实现
template<typename T1, typename T2>
void Person<T1, T2>::showPerson()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
void test01()
{
Person<string, int>p("Tom", 30);
p.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
注意:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
//类模版参数可以有默认值,注意,设置默认时,该默认值的右边都必须有默认值
template<typename NameType, typename AgeType = int> //指定默认参数为int
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Age = age;
this->m_Name = name;
}
void showPerson()
{
cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
Person<string>p("小红",18); //类模板在参数列表中有默认参数
}
int main()
{
test01();
system("pause");
return 0;
}
类模版与继承
当类模板碰到继承时,需要注意以下几点:
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中
T
的类型- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中
T
的类型,子类也需为类模板
//类模板与继承
template<typename T>
class Base
{
T m;
};
//class Son: public Base //错误,必须要知道父类中的T类型,才能继承给子类
class Son :public Base<int>
{
};
void test01()
{
Son s1;
}
//如果想灵活指定父类中T的类型,子类也需要变成类模板
template<typename T1,typename T2>
class Son2 : public Base<T2>
{
public:
Son2()
{
cout << "T1的类型为:" << typeid(T1).name() << endl;
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
T1 obj;
};
void test02()
{
Son2<int,char> s2;
}
int main()
{
test02();
system("pause");
return 0;
}
类模板分文件编写
如果工程中需要利用多个类模板,那么将这些类模板都写在同一个文件中将会导致代码可读性变差,所以有必要对类模板进行分文件编写,但是类模板的分文件编写面临着一些问题,以下是类模板分文件编写面临的问题及解决方法。
问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制的
第一种方法不写了,感兴趣的可以去看这篇博文:C++编程——类模板-CSDN博客
直接说分文件编写,利用.hpp
将person.h
和person.cpp
的内容写到一起,并将后缀名改为.hpp
,这是类模板分文件编写最常用的方式
1.编写person.hpp文件: 模板的声明和具体实现写在一起
#pragma once
#include <iostream>
using namespace std;
#include <string>
//声明
template<ctypename T1, typename T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
//具体实现
//构造函数的类外实现
template<typename T1, typename T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
//成员函数的类外实现
template<typename T1, typename T2>
void Person<T1, T2>::showPerson()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
2.编写main
函数:将hpp文件包含进来就可以了
#include <iostream>
using namespace std;
#include <string>
#include "person.hpp"
void test01()
{
Person<string, int>p("Tom", 30);
p.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
类模板与友元
- 全局函数类内实现:直接在类内声明友元即可
- 全局函数类外实现:需要提前让编译器知道全局函数的存在
1.全局函数的类内实现
template<typename T1, typename T2>
class Person
{
//全局函数类内实现,直接声明friend
friend void printPerson(Person<T1, T2> p)
{
cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
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", 30);
printPerson(p);
}
int main()
{
test01();
system("pause");
return 0;
}
2.全局函数类外实现
//提前让编译器知道Person类的存在
template<class T1, class T2>
class Person;
//类外实现
template<class T1, class T2>
void printPerson(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);
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", 30);
printPerson(p);
}
int main()
{
test01();
system("pause");
return 0;
}
注:以上类模版参照博文:C++编程——类模板-CSDN博客
标签:函数,模版,Age,C++,Person,template,模板,Name From: https://blog.csdn.net/weixin_45754224/article/details/141208683