类模板总结
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。
函数模板定义形式
由以下三部分组成: 模板说明 + 函数定义 + 函数模板调用
template < 类型形式参数表 >
类型 函数名 (形式参数表)
{
//语句序列
}
1. 模板说明
template < 类型形式参数表 >
类型形式参数的形式:
typename T1 , typename T2 , …… , typename Tn
或 class T1 , class T2 , …… , class Tn
(注:typename 和 class 的效果完全等同)
2. 函数定义
类型 函数名 (形式参数表)
{
}
注意:模板说明的类属参数必须在函数定义中出现一次
函数参数表中可以使用类属类型参数,也可以使用一般类型参数
3. 函数模板调用
max<int>(a, b); //显式类型调用
max(a, b); //自动数据类型推导
函数模板和普通函数区别结论:
两者允许并存
函数模板不允许自动类型转化
函数模板和普通函数在一起,调用规则:
1 函数模板可以像普通函数一样被重载
2 C++编译器优先考虑普通函数
3 如果函数模板可以产生一个更好的匹配,那么选择模板
4 可以通过空模板实参列表的语法限定编译器只通过模板匹配
结论:
1. 编译器并不是把函数模板处理成能够处理任意类型的函数
2. 编译器从函数模板通过具体类型产生不同的函数
2.类模板定义
类模板由模板说明和类说明构成
模板说明同函数模板,如下:
template <类型形式参数表>
类声明
例如:
template <typename Type>
class ClassName
{
//ClassName 的成员函数
private :
Type DataMember;
}
1.父类一般类,子类是模板类, 和普通继承的玩法类似
2.子类是一般类,父类是模板类,继承时必须在子类里实例化父类的类型参数
3.父类和子类都时模板类时,子类的虚拟的类型可以传递到父类中
总结:
在同一个cpp 文件中把模板类的成员函数放到类的外部,需要注意以下几点
- 函数前声明 template <类型形式参数表>
- 类的成员函数前的类限定域说明必须要带上虚拟参数列表
- 返回的变量是模板类的对象时必须带上虚拟参数列表
- 成员函数参数中出现模板类的对象时必须带上虚拟参数列表
- 成员函数内部没有限定
注意:当类模板的声明(.h文件)和实现(.cpp 或.hpp文件)完全分离,因为类模板的特殊实现,我们应在使用类模板时使用#include 包含 实现部分的.cpp 或.hpp文件。
- 类内部声明友元函数,必须写成一下形式
template<typename T>
friend A<T> addA (A<T> &a, A<T> &b);
- 友元函数实现 必须写成
template<typename T>
A<T> add(A<T> &a, A<T> &b)
{
//......
}
- 友元函数调用 必须写成
A<int> c4 = addA<int>(c1, c2);
总结:
- 从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员
- 和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化
- static 数据成员也可以使用虚拟类型参数T
6.类模板使用总结
归纳以上的介绍,可以这样声明和使用类模板:
1) 先写出一个实际的类。
2) 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的T)。
3) 在类声明前面加入一行,格式为:
template <typename 虚拟类型参数>
如:
template <typename numtype>
class A
{…}; //类体
4) 用类模板定义对象时用以下形式:
类模板名<实际类型名> 对象名;
或 类模板名<实际类型名> 对象名(实参表列);
如:
A<int> cmp;
A<int> cmp(3,7);
5) 如果在类模板外定义成员函数,应写成类模板形式:
template <typename 虚拟类型参数>
函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}
关于类模板的几点补充:
1) 类模板的类型参数可以有一个或多个,每个类型前面都必须加typename 或class,如:
template <typename T1,typename T2>
class someclass
{…};
在定义对象时分别代入实际的类型名,如:
someclass<int, char> object;
2) 和使用类一样,使用类模板时要注意其作用域,只有在它的有效作用域内用使用它定义对象。
3) 模板类也可以有支持继承,有层次关系,一个类模板可以作为基类,派生出派生模板类。
代码如下:
main.h
#include "Vector.cpp"
#include <iostream>
#include <Windows.h>
class Student
{
public:
Student(const char* _name, int _age)
{
strcpy_s(name, 64, _name);
age = _age;
}
~Student()
{
}
Student()
{
age = 0;
name[0] = NULL;
}
friend ostream& operator<<(ostream& os, const Student& object);
private:
char name[64];
int age;
};
int main(void)
{
Vector<int> studentVector(10);
for (int i = 0; i < studentVector.getLength(); i++)
{
studentVector[i] = i;
}
//依次序打印
for (int i = 0; i < studentVector.getLength(); i++)
{
cout << studentVector[i] << endl;
}
//全部打印
cout << studentVector << endl;
//float类型
Vector<float> studentVector1(10);
for (int i = 0; i < studentVector1.getLength(); i++)
{
studentVector1[i] = i * 0.1f;
}
//依次序打印
for (int i = 0; i < studentVector1.getLength(); i++)
{
cout << studentVector1[i] << endl;
}
//全部打印
cout << studentVector1 << endl;
Student s1("李小双", 28);
Student s2("俩后", 40);
Vector<Student> studentVector3(2);
studentVector3[0] = s1;
studentVector3[1] = s2;
//依次序打印
for (int i = 0; i < studentVector3.getLength(); i++)
{
cout << studentVector3[i] << endl;
}
//整体打印
cout << studentVector3 << endl;
system("pause");
return 0;
}
ostream& operator<<(ostream& os, const Student& object)
{
os << "姓名:" << object.name << "年龄:" << object.age << endl;
return os;
}
Vector.h
#include <iostream>
using namespace std;
template <typename T>
class Vector
{
public:
template <typename T>
friend ostream& operator<< <T> (ostream& os, const Vector<T>& object);
public:
Vector(int m_len=128);
~Vector();
Vector(const Vector& object);
int getLength();
T& operator[](int i);
Vector& operator=(const Vector& object);
private:
int m_len;
T* m_size;
};
template <typename T>
ostream& operator<<(ostream& os, const Vector<T>& object);
Vector.cpp
#include "Vector.h"
template <typename T>
Vector<T>::Vector<T>(int size)
{
this->m_len = size;
//分配内存大小
this->m_size = new T[m_len];
}
template <typename T>
Vector<T>::~Vector()
{
if (m_size != NULL)
{
delete[] m_size;
m_size = NULL;
m_len = 0;
}
}
template <typename T>
int Vector<T>::getLength()
{
return m_len;
}
template <typename T>
ostream& operator<<(ostream& os, const Vector<T>& object)
{
for (int i = 0; i < object.m_len; i++)
{
os << object.m_size[i] << endl;
}
return os;
}
template <typename T>
T& Vector<T>::operator[](int i)
{
return m_size[i];
}
template <typename T>
Vector<T>::Vector<T>(const Vector<T>& object)
{
if (m_size != NULL)
{
delete[] m_size;
m_size = NULL;
m_len = 0;
}
//分配内存
this->m_len = object.m_len;
this->m_size = new T[this->m_len];
//赋值
for (int i = 0; i < object.m_len; i++)
{
this->m_size[i] = object.m_size[i];
}
}
//赋值构造函数
template <typename T>
Vector<T>& Vector<T>::operator=(const Vector<T>& object)
{
if (this->m_size != NULL)
{
delete[] m_size;
m_len = 0;
m_size = NULL;
}
this->m_len = object.m_len;
this->m_size = new T[this->m_len];
for (int i = 0; i < object.m_len; i++)
{
this->m_size[i] = object.m_size[i];
}
}
posted on 2022-09-26 16:28 会飞的鱼-blog 阅读(12) 评论(0) 编辑 收藏 举报 来源
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现