类模板
1.语法:
template
类
解释:
template——声明创建模板
typename——表明后面符号是一种数据类型,可以用class替代
T——通用数据类型,名称可以替换,通常为大写字母
举例说明
class person
{
public:
person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
有没有发现一个问题,如果我们需要使用参数模板就会有T产生吧,但是这里我们的类型有两种,一个是string一个是int,那么我们怎么办?

这样就ok了
来调用一下看看效果:

2.类模板和函数模板的区别:
2.1类模板不能自动类型推导
在较老的c++版本中,类模板和函数模板几乎完全一样,但是类模板不能进行自动类型推导,只能显示指定类型。在后面版本的c++中提供了类模板自动类型推导的方法。
为什么?
类模板是抽象数据类型,类没有参数之说(构造函数的参数不是用于确定类的特性的参数),因此类是没有任何信息可以推导出应该定义什么类,因此必须明确的给出类型参数。
2.2类模板在模板参数列表中可以有默认参数
模板参数列表就是这个吧:

可以有默认参数,这里的默认参数其实是默认类型

发现没有,上面我们改成了T2=int,下面就省去了int。
那么问题就来了,如果我们把string省去的话可以这样写吗:
person p1;错误,就算是全部都有默认参数,那么我们还是必须加上尖括号。
person<> p1;正确
有一个注意事项,这个默认参数列表(类型),的用法和普通函数的默认参数的使用规则一样,如果某个位置上个有个默认的类型,那么后面都必须要有,否则会报错。
例子:

这个位置T3也因该有默认参数
3.类模板中成员函数的创建时间
类模板中的成员函数创建时机和普通欸成员函数的创建时机是有去别的:
1.普通类中的成员函数一开始就可以创建
2.类模板中的成员函数是在调用的时候才创建
4.类模板对象做函数的参数
三种传入方式:
1.指定传入的类型——直接显示对象的数据类型
#include <iostream>
#include<string>
using namespace std;
template <typename T1,typename T2>
class person
{
public:
person(T1 name, T2 age)
{
this->m_name = name;
this->m_age = age;
}
void show()
{
cout << this->m_name << " " << this->m_age;
}
T1 m_name;
T2 m_age;
};
void print1(person <string, int>p1)
{
p1.show();
}
int main()
{
person <string,int>p1("xp",123);
print1(p1);
}
直接显示数据类型体现在哪里?

2.参数模板化——将对象中的参数变为模板进行传递

把一中声明函数改成模板形式声明
3.整个类模板化——将这个对象类型模板进行传递

5.类模板与继承

会报错,必须知道父类的类型才能继承给子类。
如何解决?

在继承后面指定Base的具体类型就ok了
为什么会出现这样的问题?
因为在继承的时候如果父类中有模板参数,那么子类就不知道具体分配多少个内存空间
5.1如何灵活的指定父类中T类型,子类也需要变类模板
#include <iostream>
#include<string>
using namespace std;
template <typename T>
class Base
{
public:
T n;
};
template <typename T1,typename T2>
class son :public Base<T2>
{
public:
T1 m;
};
int main()
{
son<int, char> p();
}
这样就ok了,int先传递给T1,char传递给T2,然后T2传递给T。
6.类模板成员函数类外实现
类内声明类外实现
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;
}
注意:类模板中成员函数类外实现时,需要加上模板参数列表
7.类模板的分文件编写
问题:
类模板中函数创建时机是在调用阶段,导致分文件编写的时候链接不到
解决:
方法一:直接包含.cpp源文件
如果只包含.h中的代码,那么.cpp中的代码编译器从来都不会看到,但是你包含了.cpp又因为.cpp包含了.h,那么就得到了解决。
补充:
1.头文件(.h):
写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现。
2.源文件(.cpp):
源文件主要写实现头文件中已经声明的那些函数的具体代码。需要注意的是,开头必须#include一下实现的头文件,以及要用到的头文件。那么当你需要用到自己写的头文件中的类时,只需要#include进来就行了。
———————————————————————————————————
方法二:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名字,并不是强制的
就把函数声明和实现都写在一起就可以了
8.类模板与友元
全局函数类内实现:直接在类内声明友元就可以了

全局函数类外实现:需要提前让编译器知道全局函数的存在
#include <iostream>
#include<string>
using namespace std;
//第三个注意点,我们用了person,那么我们就必须先声明person,否则会报错
template <typename T1, typename T2>
class person;
//第二个注意点,必须要先声明这个函数,否者就将函数实现体放在最前面
template <typename T1, typename T2>
void show1(person<T1, T2>p);
template <typename T1,typename T2>
class person
{
//第四个注意点,<>,如果不加这个<>那么就会被认为是普通函数的声明而不是模板函数的声明
friend void show1<>(person<T1, T2>p);
public:
person(T1 name, T2 age)
{
this->m_name = name;
this->m_age = age;
}
T1 m_name;
T2 m_age;
};
//第一个注意点:这里的T1和T2也必须声明模板
template <typename T1, typename T2>
void show1(person<T1, T2>p)
{
cout << "名字是:" << p.m_name << "年龄是:" << p.m_age << endl;
}
int main()
{
person<string ,int> p("xp", 20);
show1(p);
}

注意点和易错点都在代码块里面,看代码块更直观。
本文详细介绍了C++中的类模板,包括其语法、使用示例、与函数模板的区别,如类模板不能自动类型推导、可有默认参数。此外,还讨论了类模板成员函数的创建时机、作为函数参数的传递方式以及类模板的继承、分文件编写、友元函数的处理。内容涵盖了从基本概念到实际应用的多个方面。
250





