C++提高编程---模板---类模板

目录

一、类模板

1.模板

2.类模板的作用

3.语法

4.声明

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

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

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

五、类模板与继承

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

七、类模板分文件编写

八、类模板与友元

九、类模板案例


一、类模板

1.模板

模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

模板是一种对类型进行参数化的工具;

通常有两种形式:函数模板和类模板;

函数模板针对仅参数类型不同的函数;

类模板针对仅数据成员和成员函数类型不同的类。

使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。

注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

2.类模板的作用

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

3.语法

template <typename  T>

4.声明

template  -- 声明创建模板

typename  --  表明其后的符号是一种数据类型,可以用clss代替

T      --  通用的数据类型,名称可以替换,通常为大写字母

示例

#include<iostream>using namespace std;// 类模板template<class name_type,class age_type>class person{public:person(name_type name,age_type age){this->name=name;this->age=age;}void show(){cout<<"姓名:"<<this->name<<"\t年龄:"<<this->age<<endl;}name_type name;age_type age;};void test01(){// 类型参数化person<string,int> p1("Ton",89);p1.show();}int main(){test01();return 0;}

运行结果:

总结:类模板和函教模板语法相似。在声明模板template后面加类,此类称为类模板

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

区别:

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板参数列表中可以有默认参数

示例:

#include<iostream>using namespace std;// 类模板template<class name_type,class age_type  = int> // 可以指定某一个通用类型具体的类型class person{public:person(name_type name,age_type age){this->name=name;this->age=age;}void show(){cout<<"姓名:"<<this->name<<"\t年龄:"<<this->age<<endl;}name_type name;age_type age;};void test01(){// 自动类型推导不行// person p1("TON",23);// 只能用指定类型person<string,int> p1("Ton",89);p1.show();}void test02(){// 类模板在模板参数列表中可以有默认值person<string> p2("Jack",90);p2.show();}int main(){test01();test02();return 0;}

运行结果:

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

类模板中成员函数和普通类中成员函数创建时机的区别:

  1. 普通类中的成员函数一开始就可以创建
  2. 类模板中的成员函数是在调用的时候才创建的

示例:

#include<iostream>using namespace std;class person1{public:void showperson1(){cout<<"person1的调用"<<endl;}};class person2{public:void showperson2(){cout<<"person2的调用"<<endl;}};template<class T>class my_class{public:T obj;// 类模板中的成员函数//   在运行前都不会创建这两个成员函数void func1(){obj.showperson1();}void func2(){obj.showperson2();}};void test01(){my_class<person1> m;m.func1();//m.func2();  运行出错,说明函数调用才会去创建成员函数my_class<person2> m1;m1.func2();}int main(){test01();return 0;}

运行结果:

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

类模板实例化出的对象,向函数传参的方式

三种传入方式:

  1. 指定传入的类型 --- 直接显示对象的数据类型(**常用)**
  2. 参数模板化         --- 将对象中的参数变为模板进行传递
  3. 整个类模板化     --- 将这个对象类型   模板化进行传递

示例:

#include<iostream>#include<typeinfo>using namespace std;/*三种传入方式:1. 指定传入的类型 --- 直接显示对象的数据类型2. 参数模板化         --- 将对象中的参数变为模板进行传递3. 整个类模板化     --- 将这个对象类型   模板化进行传递*/template<class T1,class T2>class person{public:person(T1 name, T2 age){this->name = name;this->age = age;}void show(){cout<<"姓名:"<<this->name<<"\t年龄:"<<this->age<<endl;}T1 name;T2 age;};// 1.指定传入类型void print1(person<string ,int> &p){p.show();}void test01(){// 1. 指定传入的类型 --- 直接显示对象的数据类型person<string ,int> p ("TOM",99);print1(p);}// 2.参数模板化template<class T1,class T2>void print2( person<T1,T2> &p1){p1.show();cout<<"看编译器推断的模板是什么类型"<<endl;cout<<"T1的类型为:"<<typeid(T1).name()<<endl;cout<<"T2的类型为:"<<typeid(T2).name()<<endl;}void test02(){// 2. 参数模板化         --- 将对象中的参数变为模板进行传递person <string ,int> p1 ("JACK",78);print2(p1);}// 3.将整个类模板化template<class T>void print3(T &p2){p2.show();cout<<"T的数据类型"<<endl;cout<<"T的类型为:"<<typeid(T).name()<<endl;}void test03(){person<string,int> p2("LILY",45);print3(p2);}int main(){test01();test02();test03();return 0;}

运行结果:

总结:

  • 通过类模板创建的对象。可以有三种方式向函数中进行传参
  • 使用比较广泛是第一种:指定传入的类型

五、类模板与继承

注意:

  1. 当子类继承父类是一个类模板时,子类在声明的时候,要指定出父类T的类型
  2. 如果不能确定,编译器无法给予子类分配内存
  3. 如果想灵活指定父类中T的类型,子类也需要变为类模板

示例1:

#include<iostream>#include<typeinfo>using namespace std;// 类模板与继承template<class T>class Base{T m;};// class Son:public Base  // 必须要知道父类中T的类型,才能继承给子类class Son:public Base<int>{};int main(){Son s1;return 0;}

生成成功:

示例2:

#include<iostream>#include<typeinfo>using namespace std;template<class T>class Base{T m;};// 如果想要灵活指定父类中T的类型,子类需要变类模板template<class T1,class T2>class Son2:public Base<T2>{public:Son2(){cout<<"T1的类型为:"<<typeid(T1).name()<<endl;cout<<"T2的类型为:"<<typeid(T2).name()<<endl;}T1 ojb;};void test02(){Son2<int ,char>s2;// char 传给父类,int 传给子类}int main(){test02();return 0;}

运行结果:

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

示例:

#include<iostream>using namespace std;// 类模板成员函数类外实现template<class T1,class T2>class person{public:person(T1 name,T2 age);/*{this->name=name;this->age=age;}*/void show();/*{cout<<"姓名:"<<this->name<<"\t年龄:"<<this->age<<endl;}*/T1 name;T2 age;};// 构造函数的类外实现template<class T1,class T2>person<T1,T2>::person(T1 name,T2 age){this->name=name;this->age=age;}// 成员函数的类外实现template<class T1,class T2>void person<T1,T2>::show(){cout<<"姓名:"<<this->name<<"\t年龄:"<<this->age<<endl;}void test01(){person<string,int>p("TOM",28);p.show();}int main(){test01();return 0;}

运行结果:

七、类模板分文件编写

问题:

类模板中成员函数创建的时机是在调用阶段,导致分文件编写时连接不到

解决

  1. 直接包含 .cpp 源文件
  2. 将声明和实现写到同一个文件中,并更改后缀名为 .hpp , hpp 是约定的名称,并不是强制

示例:

person.hpp文件

#include<iostream>using namespace std;// 类模板成员函数类外实现template<class T1, class T2>class person{public:person(T1 name, T2 age);T1 name;T2 age;};// 构造函数的类外实现template<class T1, class T2>person<T1, T2>::person(T1 name, T2 age){this->name = name;this->age = age;}// 成员函数的类外实现template<class T1, class T2>void person<T1, T2>::show(){cout << "姓名:" << this->name << "\t年龄:" << this->age << endl;}

.cpp文件

#include<iostream>#include"person.hpp"using namespace std;void test01(){person<string, int>p("TOM", 28);p.show();}int main(){test01();return 0;}

八、类模板与友元

类模板配合友元函数的类内实现和类外实现

  1. 全局函数类内实现 -- 直接在类内声明友元即可
  2. 全局函数类外实现 -- 需要提前让编译器知道全局函数的存在

示例:

#include<iostream>using namespace std;template<class T1,class T2>class person;// 类外实现template<class T1,class T2>void print2(person<T1,T2> p){cout<<"类外实现---姓名:"<<p.name<<"\t年龄:"<<p.age<<endl;}// 类模板与友元template<class T1,class T2>class person{// 通过全局函数打印输出// 全局函数类内实现friend void print1(person<T1,T2> p){cout<<"姓名:"<<p.name<<"\t年龄:"<<p.age<<endl;}// 全局函数类外实现// 需要加空模板的参数列表// 如果全局函数是类外实现,需要让编译器提前知道这个函数的存在friend void print2<>(person<T1,T2> p);public:person(T1 name,T2 age){this->name=name;this->age=age;}private:T1 name;T2 age;};void test01(){person<string,int>p("TOM",28);print1(p);print2(p);}int main(){test01();return 0;}

运行结果:

九、类模板案例

目的:

  1. 可以对内置数据类型以及自定义数据类型的数据进行存储
  2. 将数组中的数据存储到堆区
  3. 构造函数中可以传入数组的容量
  4. 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
  5. 提供尾插法和尾删除法对数组中的数据进行增加和删除
  6. 可以通过下标的方式访问数组中的元素
  7. 可以获取数组中当前元素的个数和数组的容量

my_array.hpp

#pragma once#include<iostream>using namespace std;// 自己的通用的数组类template<class T>class my_array{public:// 有参构造, 传入容量my_array(int copacity){cout << "my_array的有参构造" << endl;this->m_Capacity = copacity;this->m_Size = 0;this->p_Address = new T[this->m_Capacity];}//拷贝构造my_array(const my_array& arr){this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;// 浅拷贝 this->p_Address = arr. p_Address;// 深拷贝this->p_Address = new T[arr.m_Capacity];//  将arr中的数据拷贝进来for (int i = 0; i < this->m_Size; i++){this->p_Address[i] = arr.p_Address[i];}}// operator = 防止浅拷贝的问题my_array& operator=(const my_array& arr){// 先判断原来堆区是否有数据,先释放堆区数据if (this->p_Address != NULL){delete[]this->p_Address;this->p_Address = NULL;this->m_Capacity = 0;this->m_Size = 0;}this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;this->p_Address = new T[arr.m_Capacity];for (int i = 0; i < this->m_Size; i++){this->p_Address[i] = arr.p_Address[i];}return *this;}// 尾插法void Push_Back(const T& Val ){// 判断容量是否最大了if (this->m_Capacity == this->m_Size){return;}this->p_Address[this->m_Size] = Val;  // 在数组的末尾插入数据this->m_Size++;  // 更新数组的大小}// 尾删法void Pop_Back(){// 让用户访问不到最后一个元素,就是删除if (this->m_Size == 0){return;}this->m_Size--;}// 可以通过下标的方式访问数组T& operator[](int index){return this->p_Address[index];}// 返回数组的容量int get_Caoacity(){return this->m_Capacity;}// 返回数组大小int get_Size(){return this->m_Size;}// 析构函数~my_array(){if (this->p_Address != NULL){delete[] this->p_Address;this->p_Address = NULL;}}private:T* p_Address;   // 指针指向堆区开辟的真实数据int m_Capacity;   // 数组容量int m_Size;  // 数组大小};

模板array.cpp

#include"my_array.hpp"#include<iostream>using namespace std;void print_arr(my_array<int>& arr){for (int i = 0; i < arr.get_Size(); i++){cout << arr[i] << endl;}}void test01(){my_array<int> arr1(5);for (int i = 0; i < 5; i++){// 利用尾插法向数组中插入数据arr1.Push_Back(i);}cout << "arr1的打印输出" << endl;print_arr(arr1);cout << "arr1的容量为:" <<arr1.get_Caoacity()<< endl;cout << "arr1的大小为:" << arr1.get_Size() << endl;cout << "arr2的打印输出" << endl;my_array<int> arr2(arr1);print_arr(arr2);// 尾删arr2.Pop_Back();cout << "arr2的容量为:" << arr2.get_Caoacity() << endl;cout << "arr2的大小为:" << arr2.get_Size() << endl;}// 测试自定义的数据类型class person{public:person() {};person(string name,int age){this->name = name;this->age = age;}string name;int age;};void print_person_arr(my_array<person>& arr){for (int i = 0; i < arr.get_Size(); i++){cout << "姓名:" << arr[i].name << "\t年龄:" << arr[i].age << endl;}}void test02(){my_array<person> arr(10);person p1("TON", 78);person p2("JEEYU", 678);person p3("hello", 567);person p4("bgood", 567);person p5("daj", 78);// 将数据插入到数组中arr.Push_Back(p1);arr.Push_Back(p2);arr.Push_Back(p3);arr.Push_Back(p4);arr.Push_Back(p5);// 打印数组print_person_arr(arr);// 输出容量cout << "arr的容量:" << arr.get_Caoacity() << endl;// 输出大小cout << "arr的大小:" << arr.get_Size() << endl;}int main(){test01();test02();return 0;}

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640271.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试的需求人才越来越多,为什么大家还是不太愿意走软件测试的道路?

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

【动态规划】【C++算法】801. 使序列递增的最小交换次数

作者推荐 【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径 本文涉及知识点 动态规划汇总 数组 LeetCode801使序列递增的最小交换次数 我们有两个长度相等且不为空的整型数组 nums1 和 nums2 。在一次操作中&#xff0c;我们可以交换 nums1[i] 和 num…

路飞项目--03

二次封装Response模块 # drf提供的Response&#xff0c;前端想接收到的格式 {code:xx,msg:xx} 后端返回&#xff0c;前端收到&#xff1a; APIResponse(tokneasdfa.asdfas.asdf)---->{code:100,msg:成功,token:asdfa.asdfas.asdf} APIResponse(code101,msg用户不存在) ---…

学习笔记-李沐动手学深度学习(一)(01-07,概述、数据操作、tensor操作、数学基础、自动求导)

个人随笔 第三列是 jupyter记事本 官方github上啥都有&#xff08;代码、jupyter记事本、胶片&#xff09; https://github.com/d2l-ai 多体会 【梯度指向的是值变化最大的方向】 符号 维度 &#xff08;弹幕说&#xff09;2&#xff0c;3&#xff0c;4越后面维度越低 4…

Java 面向对象案例 02 (黑马)

代码&#xff1a; public class foodTest {public static void main(String[] args) {//1、构建一个数组food[] arr new food[3];//2、创建三个商品对象food f1 new food("apple","123",3.2,500);food f2 new food("pear","456",4…

临时工说:AI 人工智能化对于DBA 的工作的影响

这开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;共1900人左右 1 2 3 4 5&#xf…

ChatGPT:关于 OpenAI 的 GPT-4工具,你需要知道的一切

ChatGPT&#xff1a;关于 OpenAI 的 GPT-4工具&#xff0c;你需要知道的一切 什么是GPT-3、GPT-4 和 ChatGPT&#xff1f;ChatGPT 可以做什么&#xff1f;ChatGPT-4 可以做什么&#xff1f;ChatGPT 的费用是多少&#xff1f;GPT-4 与 GPT-3.5 有何不同&#xff1f;ChatGPT 如何…

开源堡垒机JumpServer本地安装并配置公网访问地址

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpS…

ONLYOFFICE服务器无法连接,请联系管理员问题解决

1、现象 部署好了nextcloud和onlyoffice后&#xff0c;新建文本文档报错ONLYOFFICE服务器无法连接&#xff0c;请联系管理员。 用快捷键“F12”进入控制台&#xff0c;点开错误提示栏&#xff0c;找到有“api.js“文件&#xff0c;“https://ONLYOFFICED的地址/web-apps/apps/…

书法AI全自动切字+识别算法2.0版发布,草书篆书行书楷书识别准确率超过90%,覆盖书法单字30万张

我们开发的业界识别最准覆盖作品最全的书法AI小程序上线了 书法AI全自动切字识别算法2.0版发布&#xff0c;草书篆书行书楷书识别准确率超过90%&#xff0c;准确率甩百度OCR一条街&#xff0c;覆盖书法单字30万张&#xff0c;遥遥领先同行 我们还可为客户提供书法AI全自动切字a…

借助文档控件Aspose.Words,将 Word DOC/DOCX 转换为 TXT

在文档处理领域&#xff0c;经常需要将 Word 文档转换为更简单的纯文本格式。无论是出于数据提取、内容分析还是兼容性原因&#xff0c;将 Word&#xff08;.doc、.docx&#xff09;文件转换为纯文本&#xff08;.txt&#xff09;的能力对于开发人员来说都是一项宝贵的技能。在…

87230系列USB连续波功率探头

01 87230 USB连续波功率探头 产品综述&#xff1a; 87230/87231/87232/87233系列USB功率探头是一款基于USB2.0全速/高速自适应接口的二极管检波式功率探头&#xff0c;内部采用高性能处理芯片&#xff0c;通过各种校准和补偿技术&#xff0c;使得探头具有频率范围宽、功率动…

基于SpringBoot的民宿预定管理系统 JAVA简易版

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色2.2.2 房主角色2.2.3 系统管理员角色 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿4.3 新增民宿评价4.4 查询留言4.5 新增民宿订单 五、免责说明 一、摘要 1.1 项目介绍 基于…

如何在CentOS8使用宝塔面板本地部署Typecho个人网站并实现公网访问【内网穿透】

文章目录 前言1. 安装环境2. 下载Typecho3. 创建站点4. 访问Typecho5. 安装cpolar6. 远程访问Typecho7. 固定远程访问地址8. 配置typecho 前言 Typecho是由type和echo两个词合成的&#xff0c;来自于开发团队的头脑风暴。Typecho基于PHP5开发&#xff0c;支持多种数据库&#…

重拾计网-第四弹 计算机网络性能指标

ps&#xff1a;本文章的图片内容来源都是来自于湖科大教书匠的视频&#xff0c;声明&#xff1a;仅供自己复习&#xff0c;里面加上了自己的理解 这里附上视频链接地址&#xff1a;1.5 计算机网络的性能指标&#xff08;1&#xff09;_哔哩哔哩_bilibili ​​​ 目录 &#x…

最全笔记软件盘点!你要的笔记神器都在这里:手写笔记、知识管理、文本笔记、协作笔记等!

在当今的信息化社会中&#xff0c;人们对信息的处理速度越来越快&#xff0c;从工作到生活&#xff0c;我们都面临着大量信息的冲击。在这样的环境下&#xff0c;一个能够帮助我们管理、整理和储存信息的好工具显得尤为重要&#xff0c;而笔记软件恰恰可以满足这些需求。 在选…

工作小计- RGB相关算子实现

项目中的模型一直都是直接操作NV12的yuv格式数据&#xff0c;这次的模型只支持RGB格式的输入&#xff0c;正好来自己实现对应的算子。 这里记录一下对应算子的实现过程&#xff0c;主要涉及到NV12到RGB的变换&#xff0c;RGB的crop/resize操作&#xff0c;对于数据的Norm/ToFlo…

P1068 [NOIP2009 普及组] 分数线划定————C++、Python

目录 [NOIP2009 普及组] 分数线划定题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 解题思路C CodePython Code运行结果 [NOIP2009 普及组] 分数线划定 题目描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才&#xff0c;A 市对所有报…

常用芯片学习——HC573芯片

HC573 三态输出八路透明 D 类锁存器 使用说明 锁存器是一种对脉冲电平敏感的存储单元电路&#xff0c;它们可以在特定输入脉冲电平作用下改变状态。锁存&#xff0c;就是把信号暂存以维持某种电平状态。锁存器的最主要作用是缓存&#xff0c;其次完成高速的控制器与慢速的外设…

Android studio 之 适配器

ListView仅作为容器&#xff08;列表&#xff09;&#xff0c;用于装载 & 显示数据&#xff08;即 列表项Item&#xff09;而容器内的具体数据&#xff08;列表项Item&#xff09;则是由 适配器&#xff08;Adapter&#xff09;提供 适配器&#xff08;Adapter&#xff09…