【C++】提高 -- 类模板

embedded/2024/9/20 7:28:17/ 标签: c++, 开发语言, 学习, 笔记

目录

一、类模板的作用

二、类模板的语法

三、类模板的例子

四、类模板和函数模板的区别

五、类模板中成员函数创建时机

六、类模板对象做函数参数

七、类模板与继承

八、类模板成员函数类外实现

九、类模板分文件编写

十、类模板与友元

十一、类模板案例


一、类模板的作用

建立一个通用类,类中成员、数据类型可以不具体指定,用一个虚拟的类型来代表

二、类模板的语法
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;
};
十一、类模板案例

实现以一个通用的数组类


http://www.ppmy.cn/embedded/99779.html

相关文章

Linux静态库和动态链接库的制作和使用

gcc的简单介绍 预处理(cpp) 把文本文件中的预处理指令全部展开&#xff0c;包括头文件包含&#xff0c;宏替换 gcc test.c -o test.i -E 编译(ccl) 编译预处理文件&#xff0c;生成对应硬件平台的汇编代码 gcc test.i -o test.s -S 汇编(as) 编译汇编代码&#xff0c;翻译…

前端实现首次访问,后续从本地访问

在前端实现将PDF文件下载到用户的本地磁盘&#xff0c;并在后续加载时使用本地文件&#xff0c;而不是重新从服务器下载&#xff0c;可以通过以下步骤实现&#xff1a; 1. **使用<a>标签的download属性**&#xff1a;当用户首次点击下载PDF时&#xff0c;通过<a>标…

(QT-UI)十四、在时间轴上绘制一段段时间片

本系列预计实现 ①刻度上方文字显示&#xff0c; ②时间轴拖动效果&#xff0c; ③时间轴刻度缩放&#xff0c; ④时间轴和其他控件联动显示&#xff0c; ⑤鼠标放置到时间轴&#xff0c;显示具体时间。 ⑥通过定时器&#xff0c;实时更新时间轴 ⑦时间轴上绘制时间片 完…

数据之争:网络爬虫涉及的法律问题

在大数据时代&#xff0c;除直接通过用户采集之外&#xff0c;另一大数据来源就是使用网络爬虫采集公开信息。爬虫的使用到了何种程度&#xff1f;有业内人士称&#xff0c;互联网50%以上&#xff0c;甚至更高的流量其实都是爬虫贡献的。对某些热门网页&#xff0c;爬虫的访问量…

C++ 设计模式——原型模式

原型模式 原型模式主要组成部分原型模式的使用步骤原型模式的 UML 图原型模式 UML 图解析优点和缺点适用场景总结 原型模式 原型(Prototype)模式是一种创建型模式。原型模式通过(原型对象)克隆出对个一模一样的对象。实际上&#xff0c;该模式与其说是一种设计模式&#xff0c…

【JVM】剖析字符串与数组的底层实现(二)

剖析字符串与数组的底层实现 字符串jdk8和jdk9的区别 jdk8:底层是一个char[]数组 jdk9及之后:底层是一个byte[]数组 一个中文占两个字节&#xff0c;一个char占两个字节&#xff0c;一个byte占一个字节 Jdk9及之后的版本中&#xff0c;多了一个code属性&#xff0c;这个属性标…

为IntelliJ IDEA安装插件

安装插件 插件是开发工具的扩展程序&#xff0c;通常由第三方提供&#xff0c;当安装了插件后&#xff0c;原开发工作的菜单、按钮等开发环境可能会发生变化&#xff0c;例如出现了新的菜单项&#xff0c;或出现了新的按钮&#xff0c;甚至一些全新的编码方式&#xff0c;通常…

基于langchain的prompt指令代码编写

当然可以。LangChain 是一个用于构建语言模型应用的框架&#xff0c;它支持多种语言模型&#xff0c;并提供了一系列工具和链式结构来构建复杂的语言模型应用。在这个示例中&#xff0c;我们将使用 LangChain 和 Hugging Face 的 Transformers 库来创建一个简单的应用&#xff…

基于和声搜索优化算法的机器工作调度matlab仿真,输出甘特图

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 和声记忆 (Harmony Memory, HM) 4.2 和声记忆考虑率 (Harmony Memory Consideration Rate, HMCR) 4.3 音符微调率 (Pitch Adjusting Rate, PAR) 4.4 和声记忆大小 (Harmony Memory Si…

golang 如何利用defer+recover()函数 将指定类型的panic异常转换为函数返回 error返回 使用方法示例

在golang开发的时候&#xff0c;对于业务逻辑中的某些panic异常&#xff0c;我们希望将某些不可控的panic异常转换为普通的 error并作为函数返回值返回&#xff0c; 如io 或者os中的某些操作就会导致panic异常&#xff0c;对于这个过程中的某些不可控的panic异常我们希望将某些…

RabbitMQ实现延迟消息的两种方法(提供延迟插件)

在消息队列&#xff08;MQ&#xff09;中实现延迟队列有几种常见方法。以下是两种常见的实现方式&#xff1a; 1. 使用死信队列&#xff08;DLQ&#xff09; 这种方法利用了消息的死信特性&#xff1a; 消息过期时间&#xff1a;为消息设置一个TTL&#xff08;Time-To-Live&…

git常用操作合集

1 撤销 1.1 适用场景 如果在git上提交了commit&#xff0c;但是当前提交的代码有问题&#xff0c;需要回退到上个版本 1.2 操作命令 1、git log 查看历史提交记录及对应的commit id 找到需要回退的commit id 2、执行git reset回退到之前的状态 git reset --hard <commi…

jenkins 修改访问路径

需求 由于默认的jenkins访问不安全&#xff0c;需要修改路径进行访问。 修改配置 vi /usr/lib/systemd/system/jenkins.service 修改变量JENKINS_PREFIX Environment"JENKINS_PREFIX/dev-jenkins"重新加载配置 systemctl daemon-reload 重启jenkins systemctl restar…

git的介绍

git简介 git的作者&#xff1a;林纳斯 托瓦兹&#xff0c;Linux之父。 Linux&#xff1a;1991年。 1.版本控制简介 1.1.文件的版本 人肉版本控制缺点 操作麻烦, 每次都需要复制 → 粘贴 → 重命名无法通过文件名知道具体做了哪些修改容易丢失, 如果硬盘故障或不小心删除 …

ArrayList底层扩容机制详解保姆级

我们对下面代码进行debug&#xff0c;当我们使用无参构造器时&#xff0c;也就是没用指定ArrayList的容量的时候他他是空的&#xff0c;当我们第一次添加的时候才会扩容为10&#xff0c;当容量满之后每次会以1.5倍进行扩容。 当我们new之后elementData是空的 &#xff0c;只有执…

在遍历过程中修改 List 的几种方式

引言 在 Java 中&#xff0c;遍历集合&#xff08;如 List&#xff09;时直接修改集合元素通常是安全的&#xff0c;但如果尝试在遍历过程中添加或删除元素&#xff0c;则可能导致 ConcurrentModificationException 异常。本文将探讨如何安全地在遍历过程中修改 List&#xff…

从零开始学习SLAM(五):极几何与极约束

文章参考计算机视觉life 前备知识 概念 几何关系&#xff1a; 上图中&#xff1a; 极平面&#xff08;Epipolar plane&#xff09;&#xff1a;点c0, c1, p三点确定的平面&#xff1b; 极点&#xff08;Epipoles&#xff09;&#xff1a; c0 c1 连线与两个平面的交点 基线&a…

什么是布隆过滤器,实现原理是什么?

背景介绍 在互联网中&#xff0c;我们经常遇到需要在大量数据中判断目标数据是否存在的情况。例如&#xff0c;在网络爬虫中&#xff0c;我们需要判断某个网址是否已经被访问过。为了实现这一功能&#xff0c;通常需要使用一个容器来存储已访问过的网址。如果将这些数据直接存…

分布式 - 主从复制技术详解及时延处理

作者&#xff1a;逍遥Sean 简介&#xff1a;一个主修Java的Web网站\游戏服务器后端开发者 主页&#xff1a;https://blog.csdn.net/Ureliable 觉得博主文章不错的话&#xff0c;可以三连支持一下~ 如有疑问和建议&#xff0c;请私信或评论留言&#xff01; 主从复制技术详解及时…

C/C++语言基础--指针三大专题详解3,完结篇(包括指针做函数参数,函数指针,回调函数,左右法则分析复杂指针等)

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 指针是C/C的灵魂&#xff0c;和内存地址相关联&#xff0c;运行的时候速度快&#xff0c;但是同时也有很多细节和规范要注意的&#xff0c;毕竟内存泄漏是很恐怖的指针打算分三篇文章进行讲解&#xff0c;本…