Th4.2:类模板的概念、类模板的定义和使用之详述

2023-11-07

本小节回顾的知识点分别是类模板的概念、类模板的定义和使用

今天总结的知识分为以下4个点:
(1)概述
(2)类模板定义
(3)类模板的成员函数
(4)非类型模板参数

(1)概述:

        类模板:就是包含待指定数据类型的类,这个待指定的数据类型就是类模板的模板参数

        注意:我们必须为类模板提供<模板参数表>,这样才能够让编译器用其实例化(生成)成一个特定版本的类。(编译器不能够为类模板的模板参数do自动类型推断!

        为什么会提出类模板这种概念呢?答:类模板这种东西,实现了让不同数据类型都适用于同一套类的代码的目的!这样你就不用说为了某个特定的数据类型的兼容性,特意去写一套类的代码以适用于该数据类型的数据了。这样do的话你程序的可读性将大大增加,同时代码的维护难度也大大降低!

        比如:vector<int>、vector<double>、vector<Person>等等。。。

(2)类模板定义:

        声明格式:

template<typename T1, typename T2, ...typename Tn>
class ClassName {
public:
	/*...*/
};

        我在4.1小节就已经总结过:对于模板(函数/类)代码,除非编译器看到你使用了该模板,否则都是不生成对应特定版本的模板代码的!而当要生成模板代码时,就需要找到该类模板的类定义实现codes or 函数模板的定义实现codes,那么此时我们把模板的代码放在.h头文件中,就算非常合适不过的了!Especially,对于类模板而言,我们只有类模板all类成员声明以及类成员定义实现都放在一个头文件中时,才能在后续要用到该类模板时,能一次过让编译器知道该类模板的all信息,也只有这样才能让编译器为我们成功生成一个指定数据类型的特殊版本的类。因此在VS2022编译器下,建议,我们直接将包含上述两部分代码的XXX.h头文件直接改名为XXX.hpp头文件(.hpp 可理解为 .h + .cpp的结合)。(当然,你不改为.hpp,依然在一个.h头文件中去写一个类模板的all类成员声明以及类成员定义实现也是可以的,非强制性。)

请看以下代码:

myVector.h:

#ifndef __MYVECTOR_H__
#define __MYVECTOR_H__
#include<iostream>
using namespace std;
template<typename T>
class myVector {
private:
	int m_Size;//当前数组元素个数
	int m_Capacity;//数组总容量大小
	T* my_Arr;
public:
	typedef T* myiterator;//迭代器 vector iterator
	myiterator mybegin() {//迭代器的起始位置my_Arr[0]
		return &my_Arr[0];
	}
	myiterator myend() {//迭代器的最后一个元素my_Arr[n-1]的下一个位置
		return &my_Arr[m_Size];
	}
	myVector() :m_Size(0), m_Capacity(0), my_Arr(nullptr) {}
	myVector(int n) :m_Size(0), m_Capacity(n) {//指定数组大小为n
		my_Arr = new T[n];//heap区开辟一个数组,包含n个类型为T的元素
	}
	//在类模板内部使用类名时是不需要指定模板参数的!
    //==>myVector<T>& operator=(const myVector<T>& vec){...}
	myVector& operator=(const myVector& vec) {
		if (my_Arr != nullptr) {
			delete[] my_Arr;//你new一个数组和delete一个数组要配对
            my_Arr = nullptr;//防止my_Arr变成野指针乱指向
		}
		this->m_Size = vec.getSize();
		this->m_Capacity = vec.getCapacity();
		my_Arr = new T[m_Capacity];
		int i = 0;
		for (auto x : vec) {
			my_Arr[i++] = x;
		}
	}
	int getCapacity();
	int getSize();
	void Push_back(T nums);
	void Pop_back();
	T& operator[](int index);
	~myVector();
};
#endif __MYVECTOR_H__
template<typename T>
int myVector<T>::getCapacity() {
	return this->m_Capacity;
}
template<typename T>
int myVector<T>::getSize() {
	return this->m_Size;
}
template<typename T>
void myVector<T>::Push_back(T nums) {
	//未满时 push进来
	if (m_Size < m_Capacity) {
		//...
		m_Size++;
	}
	//满了 动态扩容 再push进来
	//...             
}
template<typename T>
void myVector<T>::Pop_back() {
	//未空时 pop掉my_Arr[n-1]这个数组元素!也即pop掉数组的last一个元素!
	if (m_Size > 0) {
		//...
		m_Size--;
	}
	//空了 直接return;
	cout << "myVector已空!无法从中pop元素!" << endl;
	return;
}
template<typename T>
T& myVector<T>::operator[](int index) {
	return my_Arr[index];
}
template<typename T>
myVector<T>::~myVector<T>() {
	if (my_Arr != nullptr) {
		delete[] my_Arr;
		my_Arr = nullptr;//防止my_Arr变成野指针乱指向
	}
}

main.cpp中:

#include<iostream>
#include"myVector.h"
using namespace std;
int main(void) {
	myVector<int> vec;
	//这个时候,编译器就会为我们生成一个特定版本的myVector类!

	vec.Pop_back();
	vec.Push_back(1);
	vec.getSize();
	vec.getCapacity();
	return 0;
}

(3)类模板的成员函数:

        在类模板的定义{内部}写类模板的成员函数时,和写普通的类的成员函数没区别。

        在类模板的定义{}的外部写类模板的成员函数时,就必须像类似函数模板那样去写该类模板的成员函数。

        注意:在类模板之外写其成员函数时,必须把模板参数列表<>里的东西写全写完整!!!

        格式:

template<typename T1,typename T2,...,typename Tn>
retName templateClassName<T1,T2,...Tn>::memberFuncName(Params){
    /*...*/
}

        注意:类模板的成员函数只有当被调用时,才会给编译器实例化生成一份特定的成员函数的codes,供你的调用该成员函数的语句使用!你若不调用某个类模板的成员函数,编译器就不会为你生成特定版本的该成员函数的代码!一句话:对于一个实例化了的类模板,其成员函数只有被使用时才会给编译器实例化。

请看以下代码:

#ifndef __PERSON_H__
#define __PERSON_H__
#include<iostream>
#include<string>
using namespace std;
template<typename T>
class Person {
public:
	int m_Age;
	string m_Name;
	T girlFriendNums;
	Person(int age, string name) :m_Age(age), m_Name(name), girlFriendNums(0) {}
	//在类模板定义之内定义其成员函数
	void showInfo() {
		cout << "Name: " << m_Name << ",Age:" << m_Age << endl;
		cout << "girlFriendNums: " << girlFriendNums << endl;
	}
	int getAge();
	string getName();
	T getGrilFNums();
	~Person() {}
};
//在类模板定义之外定义其成员函数
template<typename T>
int Person<T>::getAge() {
	return this->m_Age;
}
template<typename T>
string Person<T>::getName() {
	return this->m_Name;
}
template<typename T>
T Person<T>::getGrilFNums() {
	return this->girlFriendNums;
}
#endif __PERSON_H__

(4)非类型模板参数:

        非类型模板参数:当普通数据(int/double/self-defineType)类型作为<模板参数列表>中的参数时,它就可称之为非类型的模板参数了。

//myarr.h

#ifndef __MYARR_H__
#define __MYARR_H__
#include<iostream>
using  namespace std;
//这里的Size为非模板参数类型
template<typename T,int Size=10>
class myArr {
private:
	T arr[Size];
public:
	//...
	void myfunc();
};
template<typename T,int Size>//在类模板之外定义成员函数时,必须把模板参数列表写全!
void myArr<T,Size>::myfunc() {
	cout << Size << endl;
}
#endif __MYARR_H__

//main.cpp

#include<iostream>
#include"myarr.h"
using namespace std;
int main(void) {
	myArr<int,100> myarr;//一个int型且大小为100的数组
	myarr.myfunc();//100
	myArr<double> myarr2;//一个double型且大小为10的数组
	myarr2.myfunc();//10
	return 0;
}

        注意①:在类模板写成员函数时,不能给其模板参数列表中的参数指定默认值!

        比如:

        注意②浮点型(float/double)不能作为类模板的非类型模板的参数!

        比如:

        注意③类 类型(内置标准库中的类/self-define类)都不能作为类模板的非类型模板的参数!

        比如:

        以上就是我总结的关于类模板的概念、类模板的定义和使用的笔记。希望你能读懂并且消化完,也希望自己能牢记这些小小的细节知识点,加油吧,我们都在coding的路上~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Th4.2:类模板的概念、类模板的定义和使用之详述 的相关文章

  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • 确保 StreamReader 不会挂起等待数据

    下面的代码读取从 tcp 客户端流读取的所有内容 并且在下一次迭代中它将仅位于 Read 上 我假设正在等待数据 我如何确保它不会在没有任何内容可供读取时返回 我是否必须设置低超时 并在失败时响应异常 或者有更好的办法吗 TcpClient
  • 提交后禁用按钮

    当用户提交付款表单并且发布表单的代码导致 Firefox 中出现重复发布时 我试图禁用按钮 去掉代码就不会出现这个问题 在firefox以外的任何浏览器中也不会出现这个问题 知道如何防止双重帖子吗 System Text StringBui
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • C中的malloc内存分配方案

    我在 C 中尝试使用 malloc 发现 malloc 在分配了一些内存后浪费了一些空间 下面是我用来测试 malloc 的一段代码 include
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • 如何区分用户点击链接和页面自动重定向?

    拥有 C WebBrowser control http msdn microsoft com en us library system windows forms webbrowser aspx在我的 WinForms 应用程序中 并意识
  • 获取两个工作日之间的天数差异

    这听起来很简单 但我不明白其中的意义 那么获取两次之间的天数的最简单方法是什么DayOfWeeks当第一个是起点时 如果下一个工作日较早 则应考虑在下周 The DayOfWeek 枚举 http 20 20 5B1 5D 3a 20htt
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 回发后刷新时提示确认表单重新提交。我做错了什么?

    我有一个以空白 默认状态启动的仪表板 我让用户能够将保存的状态加载到仪表板中 当他们单击 应用 按钮时 我运行以下代码 function CloseAndSave var radUpload find radUpload1ID var in
  • 由 IHttpClientFactory 注入时模拟 HttpClient 处理程序

    我创建了一个自定义库 它会自动为依赖于特定服务的 Polly 策略设置HttpClient 这是使用以下方法完成的IServiceCollection扩展方法和类型化客户端方法 一个简化的例子 public static IHttpClie
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 在 Visual Studio 2010 中从 Fortran 调用 C++ 函数

    我想从 Fortran 调用 C 函数 为此 我在 Visual Studio 2010 中创建了一个 FORTRAN 项目 之后 我将一个 Cpp 项目添加到该 FORTRAN 项目中 当我要构建程序时出现以下错误 Error 1 unr
  • 为什么调用非 const 成员函数而不是 const 成员函数?

    为了我的目的 我尝试包装一些类似于 Qt 共享数据指针的东西 经过测试 我发现当应该调用 const 函数时 会选择它的非 const 版本 我正在使用 C 0x 选项进行编译 这是一个最小的代码 struct Data int x con
  • AES 128 CBC 蒙特卡罗测试

    我正在 AES 128 CBC 上执行 MCT 如中所述http csrc nist gov groups STM cavp documents aes AESAVS pdf http csrc nist gov groups STM ca
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • 如何在非控制台应用程序中查看 cout 输出?

    输出到调试窗口似乎相当繁琐 我在哪里可以找到cout如果我正在编写非控制台信息 则输出 Like double i a b cout lt lt b lt lt endl I want to check out whether b is z
  • C++ 函数重载类似转换

    我收到一个错误 指出两个重载具有相似的转换 我尝试了太多的事情 但没有任何帮助 这是那段代码 CString GetInput int numberOfInput BOOL clearBuffer FALSE UINT timeout IN
  • WebSocket安全连接自签名证书

    目标是一个与用户电脑上安装的 C 应用程序交换信息的 Web 应用程序 客户端应用程序是 websocket 服务器 浏览器是 websocket 客户端 最后 用户浏览器中的 websocket 客户端通过 Angular 持久创建 并且

随机推荐

  • 百度文本内容审核

    百度提供了免费的文本内容审核 响应时间在1秒左右 第一步 在百度开发账号中创建应用 获取配置信息 百度图片审核配置信息 public class BaiduSensitiveConfig 1 app id public static fin
  • 关于BatchNorm,我们需要了解什么(一)

    一 前言 准备写一个系列的文章 关于BatchNorm 本文的主要内容是对BN技术做一个汇总 默认读者已经了解BN层的主要工作原理 理解BN算法中每个参数的含义以及在训练和推理过程中的算法原理 1 BN存在的问题以及在必须使用BN的时候 我
  • powerDesigner反向生成数据建模图

    添加sql脚本文件 The file has been successfully reverse engineered 保存dpm文件 生成报告 建议标准模式 html就行
  • php文件注入思路

    题目描述 打开题目给定url 发现有一个文件上传按钮 猜测可能是需要上传php文件对服务器进行查看 解题步骤 1 上传一个文档文档 提示无法上传 2 上传一张jpg图像 发现给定了图像存储的路径 3 于是使用浏览器权限绕过的方式 上传php
  • Android7.1.2修改以太网静态设置

    如何设置以太网有线网络静态配置 从源码看设置以太网配置调用的是EthernetManager的setConfiguration方法 所以我们就只需要创建IpConfiguration这个实例 他的构造方法传入值是 最终配置的参数就是Stat
  • 如何系统学习k8s?

    1 理解基本概念 Kubernetes 有很多复杂的概念 例如 Pod Deployment Service Ingress 等 了解这些基本概念是系统学习 k8s 的第一步 一些有用的资源包括 Kubernetes官方文档 k8s io网
  • Failed to deserialize payload. Is the byte array a result of corresponding serialization for Default

    这是java redis反序列化问题 Cannot deserialize nested exception is org springframework core serializer support SerializationFaile
  • 怎么注册自媒体账号?技巧分享

    自媒体行业的快速发展 相信大家都有耳闻 至于自媒体应该如何有效地运营 相信大家就纷纷摸不着头脑了那么新手如何做自媒体呢 我归纳分为这几个步骤 一 自媒体账号定位 在做自媒体之前 一定需要做好定位 比如我这个自媒体开通了要拿来做什么 比如给人
  • 强大的Source Insight查找操作和代码查看

    系列文章 一 Source Insight 简介 常用设置和常用的快捷键 二 Source Insight 工程操作 三 Source Insight 窗口介绍 四 强大的Source Insight查找操作和代码查看 五 Source I
  • Logcat 的常用命令说明

    个人认为有一下几个常用命令 adb logcat c 清除所有以前的日志 adb logcat d 这个命令是在时间上倒过来用的 就是你先操作 然后敲这个命令 打出来的log就是你刚操作那段时间的log 而且自动退出log模式 adb lo
  • Vue PostCss插件——autoprefixer,自动补全css浏览器前缀

    Autoprefixer是一款基于PostCSS插件 用于解析CSS并使用Can I Use中的值向CSS规则添加供应商前缀 它是 Google 推荐的 并在Twitter和阿里巴巴中使用 可以实现css3代码自动补全 也可以运用到sass
  • 实例:vmem_disk驱动-->vmem_disk驱动模块的加载与卸载(2)

    支持 制造请求 请求队列 static void setup device struct vmem disk dev dev int which memset dev 0 sizeof struct vmem disk dev dev gt
  • 达蒙DM数据库使用经验

    DM表 字段注释 注 dm数据库无法在建表的同时为字段名添加注释 为表添加注释 comment on table 库名 表名 is 表注释 为表字段添加注释 comment on column 库名 表名 列名 is 列注释 DM查询错误
  • 如何搭建一个CRM系统

    搭建一个客户关系管理系统 CRM 需要以下几个步骤 定义需求 明确您对 CRM 系统的要求 以便确定它应该具有哪些功能和特性 选择技术 根据您的需求 选择合适的技术平台 例如使用自主开发 购买现成软件或使用云 CRM 服务 数据收集 收集所
  • QT中如何在主窗口中添加子窗口

    1 方法 原理其实很简单 和在窗口上动态 代码的形式 添加控件的方法一样 但需要设置一下子窗口的属性 在子窗口构造函数中添加代码 setWindowFlags Qt FramelessWindowHint 作用 隐藏子窗口的标题栏和边框 如
  • 统计各专业学生人数

    题目描述 学生表 专业表 SELECT dept name ifnull z studen cou 0 AS 人数 FROM SELECT dept id COUNT AS cou FROM z studen GROUP BY dept i
  • 一种设置python函数执行超时时间

    问题来源 写该文章的缘由是因为最近回答CSDN用户一个关于函数超时的问题 当时用户提问 最近想用python实现如下的一个功能 即设置函数的超时时间 当超过这个时间后函数退回到父函数而不会引发异常或导致整个程序结束 当时用户还贴出了另一篇文
  • Python 朴素贝叶斯(Naive Bayes)分类

    Na ve Bayes 分类的核心是计算条件概率P y x 其中y为类别 x为特征向量 其意义是在x样本出现时 它被划分为y类的可能性 概率 通过计算不同分类下的概率 进而把样本划分到概率最大的一类 根据条件概率的计算公式可以得到 P y
  • oracle_11 linux客户端安装说明以及如何添加sqlldr命令

    1 安装文件准备 oracle instantclient11 2 basic 11 2 0 2 0 x86 64 rpm oracle instantclient11 2 devel 11 2 0 2 0 x86 64 rpm oracl
  • Th4.2:类模板的概念、类模板的定义和使用之详述

    本小节回顾的知识点分别是类模板的概念 类模板的定义和使用 今天总结的知识分为以下4个点 1 概述 2 类模板定义 3 类模板的成员函数 4 非类型模板参数 1 概述 类模板 就是包含待指定数据类型的类 这个待指定的数据类型就是类模板的模板参