C++第十二节课 模板初阶和string引入

一、函数模板

我们不需要写具体的函数,而是写这个函数的模板,编译器会根据模板生成对应的函数;

template<typename T>
template<class T>

两者的作用是等效的!

用模板完成的功能有时候也叫泛型编程;

如果我们要传递多种类型的参数:

template<typename T1,typename T2>
void Func(const T1& x, const T2& y)
{cout << x << " " << y << endl;
}
int main()
{Func(1, 2.2);return 0;
}

可以通过定义多个模板参数来实现;

函数模板根据调用,自己退到模板参数的类型,实例化出对应的函数;

问题:模板参数可以用typename / class,那么可以用struct吗?

答案:不可以!规定不可以!

1、函数模板的实例化

模板参数的实例化分为两种:隐式实例化和显式实例化!

隐式实例化:让编译器根据实参推演模板参数的实际类型!

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);// 下面这种编译不通过!// Add(a1, d1);
}

这里只有一个模板参数,实例化的时候不知道该参数的类型为int还是double,会产生歧义!

有两种解决方法:

  • 用户自己强制转化;
  • 显示实例化

用于自己强制转化:

	// 方法一:强制类型转换cout << Add(a1, (int)d1) << endl;cout << Add((double)a1, d1) << endl;

这里是根据实参传递的类型,推导T的类型 

显示实例化:

	// 方法二:显示实例化cout << Add<int>(a1,d1) << endl;cout << Add<double>(a1, d1) << endl;

这里是指定T的类型进行实例化;(会存在隐式类型的转换 --- 产生临时变量 --- 临时变量具有常属性 --- 因此参数需要加上const进行修饰!)

但是有一种情况必须使用显式实例化:

template<class T>
T* Alloc(int a)
{return new T[a];
}

这与这个函数,我们如果要调用,必须使用显示实例化,因为参数的类型已经被指定,返回值T*不确定,此时应该指定T的类型;

double* p1 = Alloc<double>(a1);

只能通过显示实例化来调用!

2、模板参数的匹配原则

1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

int Add(int left, int right)
{return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{return left + right;
}
void Test()
{Add(1, 2); // 与非模板函数匹配,编译器不需要特化Add<int>(1, 2); // 调用编译器特化的Add版本
}

2. 对于非模板函数同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板;

int Add(int left, int right)
{return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}
void Test()
{Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

二、类模板

 我们分析下下面的代码:

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};
void TestStack()
{Stack s;s.Push(1);s.Push(2);
}

如果我们改变一个栈中存放的数据类型;

typedef int DataType;

我们需要将int改为其他类型;

但是如果对于下面这种情况:

int main()
{Stack s1;   // intStack s2;   // doublereturn 0;
}

栈s1要存放int类型;

栈s2要存放double类型;

如果不使用模板我们只能拷贝多份相同的代码用于存放不同的类型!

这时候我们可以将上面的代码改为模板形式的:

template<class T>
class Stack
{
public:Stack(size_t capacity = 3){/*_array = (T*)malloc(sizeof(T) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}*/_array = new T[capacity];_capacity = capacity;_size = 0;}void Push(const T& data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:T* _array;int _capacity;int _size;
};

类模板的实例化

此时如果我们需要创建对象,因为构造函数里面不一定有参数!因此我们不能隐式实例化的方式让编译器去猜,只能自己显示实例化

因此我们这样子实例化:

int main()
{Stack<int> s1();   //intStack<double> s2();  // doubleStack<char> s3();  //charreturn 0;
}

注意点:类模板中函数的声明和定义与之前的不太一样:

template<class T>
class Stack
{
public:Stack(size_t capacity = 3);void Push(const T& data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:T* _array;int _capacity;int _size;
};Stack(size_t capacity = 3)
{/*_array = (T*)malloc(sizeof(T) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}*/_array = new T[capacity];_capacity = capacity;_size = 0;
}

如果我们直接在类外面对构造函数进行定义,此时编译器无法识别T!

template<class T>
class Stack
{
public:Stack(size_t capacity = 3);void Push(const T& data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:T* _array;int _capacity;int _size;
};template<class T>
Stack<T>::Stack(size_t capacity = 3)
{/*_array = (T*)malloc(sizeof(T) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}*/_array = new T[capacity];_capacity = capacity;_size = 0;
}

注意点:对于普通类还是,类型和类名是一样的!

例如:Date既是类型,也是类名!

但是类模板的类型和类名不相同!例如:

  • Vector类名
  • Vector<int>才是类型

 template的作用范围可以认为是跟接下来的函数或者类的作用域(一个对应的花括号内);

 每个函数都需要写对应的模板参数!

三、string类

使用string类需要包含头文件:#include<string>

且string这个类位于标准库中,因此如我们可以采用下面两种形式:

int main()
{string s1;std::string s2;return 0;
}

1、string的构造函数

int main()
{string s1;   // 使用默认构造string s2("张三");string s3("hello");string s4(10, 'a');    // 复制n个字符;string s5(s2);   // 调用拷贝构造return 0;
}

这些是较为常用的重载的构造函数;

分析上面这个拷贝构造函数:

拷贝一个对象从pos位置的len长度!------ 部分拷贝(len在这里为缺省值!pos是对应字符串的下标,与数组类似)

npos是什么?

这里的npos是一个公有的静态的成员变量,它的值为-1!

但实际上,它的值不等于-1,而是整形的最大值!

因为size_t 默认参数都是正数,此时是把一个负数传递给一个无符号数,会发生整型提升,提升后,len会变成size_t的最大值,且在32 位的系统中,size_t 的最大值是 4294967295,而在 64 位的系统中则最大为 18446744073709551615。

string s7(s3, 6);

因此,如果我们没有给第三个参数,默认的第三个参数的值非常大,也就是默认拷贝之后的所有内容! 

接下来我们尝试调用第三种构造函数重载的形式:

int main()
{string s1;   // 使用默认构造string s2("张三");string s3("hello world");string s4(10, 'a');    // 复制n个字符;string s5(s2);   // 调用拷贝构造string s6(s3, 6, 5);cout << s3 << endl;cout << s6 << endl;return 0;
}

并且可以发现string这个类重载了流输出,我们可以直接cout输出流;

并且,因为string类中重载了运算符,如果我们需要对两个字符串进行比较,我们可以采用下面的方法:

	cout << (s1 == s2) << endl;cout << (s1 < s2) << endl;

注意,流插入和流提取运算符的优先级比较高,因此这里我们需要加上括号!

假设我们需要将下面的字符串拆分为三部分:

	string ur1("https://cplusplus.com/reference/string/string/string/");string sub1(ur1, 0, 5);string sub2(ur1, 8, 13);string sub3(ur1, 22, ur1.size()-22);//string sub3(ur1, 22);cout << sub1 << endl;cout << sub2 << endl;cout << sub3 << endl;

如果没有缺省值,那么我们就得采用上面的方法,但是有了缺省值就非常方便;

分析下面的拷贝构造函数:

意思为:拷贝字符数组s的前n个字符进行初始化;

2、赋值运算符重载

对于赋值运算符,我们可以采用下面三种形式:

	s1 = s2;cout << s1 << endl;s1 = "11111";cout << s1 << endl;s1 = '2';cout << s1 << endl;

3、增删查改中的增

对于一个字符串,我们我们想向其中插入值:

int main()
{string s1("hello");// 尾插一个字符s1.push_back(' ');// 尾插一个字符串s1.append("world");cout << s1 << endl;return 0;
}

 使用push_back可以完成工作,但是还可以使用运算符重载“+=”:

int main()
{string s1("hello");// 尾插一个字符//s1.push_back(' ');尾插一个字符串//s1.append("world");//使用运算符重载s1 += " ";s1 += "world";cout << s1 << endl;return 0;
}

也能达到目标!

问题:假设有一个正整数x,要求将x转为string对象,怎么实现?

int main()
{size_t x;cin >> x;string xstr;while (x){size_t val = x % 10;xstr += ('0' + val);x /= 10;}cout << xstr << endl;return 0;
}

但是这里得到的结果是反的,因此我们需要逆置,且考虑x为0的情况,具体情况我们下次再讲!

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【分立元件】案例:新人加了个TVS管为什么可能导致系统不能正常工作

因为最近在带多个新人,让其设计原理图和PCB总会发现各种电路问题点。比如TVS管接法问题。 TVS是一种限压型的过压保护器,它将过高的电压钳制至一个安全范围,藉以保护后面的电路,有着比其它保护元件更快的反应时间,这使TVS可用在防护lighting、switching、ESD等快速破坏性瞬…

JAVA虚拟机----JVM

(一)认识JVM JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。 虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。 常⻅的虚拟机&#xff1a;JVM、VMwave、Virtual Box。 &#xff08;二&#xff09;JVM运…

Linux进阶命令-重定向

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 经过上一章Linux日志的讲解&#xff0c;我们对Linux系统自带的日志服务已经有了一些了解。我们接下来将讲解一些进阶命令&am…

5、PointNeXt

5、PointNeXt PointNeXt论文&#xff1a;PointNeXt 关于PointNeXt实际上仅仅是在PointNet的基础上做了一些改进&#xff0c;从它的全称就可以看出&#xff0c;Revisiting PointNet with Improved Training and Scaling Strategies&#xff0c;在PointNet的基础上&#xff0c;引…

前端常用的主流框架有哪些

前端开发中&#xff0c;有几个主流框架非常受欢迎&#xff0c;它们为开发者提供了丰富的功能和高效的开发体验。以下是一些当前最常用的前端主流框架&#xff1a; React&#xff1a; React 是由 Facebook 开发的一个用于构建用户界面的 JavaScript 库。它鼓励使用组件化的开发模…

O1-preview:智能预测与预取驱动的性能优化处理器设计OPEN AI

# 创作不易&#xff0c;您的打赏、关注、点赞、收藏和转发是我坚持下去的动力&#xff01; O1-preview 是一种用于性能优化的处理器设计原理&#xff0c;主要通过智能预测和数据预取来提升处理器的执行效率。以下是对 O1-preview 原理的详细介绍&#xff0c;以及它相对于以往的…

微波无源器件 功分器 4 一种用于天线阵列的紧凑宽带四路双极化波导功分器

摘要&#xff1a; 一种新型紧凑和高效率&#xff0c;在一个同相2x4方案(四路)显示双极化的功分器的设计和仿真被提出了&#xff0c;两个基本的正交模式TE10和TE01在四个方波导处同相输出通过使用四个3端口个四个E面和两个H面功分结构。此功分末端接了两个商用波导(WR75)端口&am…

插入排序详解

思路 把后面的值&#xff08;temp&#xff09;与前面的值&#xff08;end&#xff09;做对比&#xff0c; 若temp位置的值小于end位置的值&#xff0c; end位置的值给end1位置。。 语言难以描述&#xff0c;请大家看下图。 代码 void InsertSort(int *arr, int n) {/*为了…

django开发流程

一、官方网站&#xff1a; Django documentation | Django documentation | Djangohttps://docs.djangoproject.com/en/5.1/ 1.安装 django : pip install django 2. django项目的配置文件 (settings.py) BASE_DIR 项目根路径 DEBUG 调试模式 INSTALLE…

旷视轻量化网络shufflenet算法解读

目录 预备知识 1. 回顾MobileNet V1的核心思想---深度可分离卷积 2.ShuffleNet主要有两个创新点 2.1 分组卷积与11分组卷积 2.2 channel Shuffle&#xff08;通道重排&#xff09; 2.3 通道重排过程 3. ShuffleNet网络结构 3.1 ShuffleNet unit 3.2 不同分组数的Shu…

AlexNet项目图片分类通用模型代码

目录 一&#xff1a;建立AlexNet模型&#xff08;在model文件中写&#xff09; 1.构造5层卷积层 2.构造3层神经网络层 3.forward函数 4.模型最终代码 二&#xff1a;训练数据&#xff08;在train中写&#xff09; 1.读出数据 2.训练 3. 测试模型更新参数 4.完整的训练…

问题——IMX6UL的uboot无法ping主机或Ubuntu

主要描述可能的方向&#xff0c;不涉具体过程&#xff0c;详细操作可以查阅网上相关教程 跟随正点原子教程测试以太网端口时&#xff0c;即便按照步骤多次尝试也无法ping通&#xff0c;后补充了些许网络工程基础知识解决了这个问题。 uboot无法ping主机或Ubuntu有多种可能&…

Redis集群知识及实战

1. 为什么使用集群 在哨兵模式中&#xff0c;仍然只有一个Master节点。当并发写请求较大时&#xff0c;哨兵模式并不能缓解写压力。我们知道只有主节点才具有写能力&#xff0c;那如果在一个集群中&#xff0c;能够配置多个主节点&#xff0c;是不是就可以缓解写压力了呢&…

总结拓展十:SAP开发计划(下)

第一节 接口功能开发说明书设计 1、软件系统接口作用 答&#xff1a;系统接口&#xff0c;是实现系统间数据传输的功能。 2、软件系统接口特点 1&#xff09;采用Web Service技术作为平台&#xff0c;有众多的数据传输协议标准&#xff0c;通过API与外界交流数据。 2&…

Vscode搭配latex简易教程

1. 找镜像网站下载texlive的iso文件 清华源镜像 下载之后直接打开iso文件&#xff0c;打开install-tl-windows.bat文件&#xff0c;进行安装即可&#xff0c;安装大概30分钟左右 2. VScode端配置 2.1 下载这三个插件 2.2 打开设置 2.3 追加内容到配置json文件当中 // Latex…

14_input子系统my_touch_device,my_touch_handlerLinux内核模块

01_basicLinux内核模块_the kernel was built by:x86 64-linux-gnu-gcc-12(ub-CSDN博客文章浏览阅读678次&#xff0c;点赞3次&#xff0c;收藏3次。环境IDubuntuMakefilemodules:clean:basic.creturn 0;运行效果。_the kernel was built by:x86 64-linux-gnu-gcc-12(ubuntu 12…

贷款年利率迷局:年利率3.8%为何变成2.07%?

朋友们&#xff0c;聊聊贷款那点事儿&#xff0c;特别是那个让人又爱又恨的年利率&#xff0c;听起来简单&#xff0c;3.8%就是一年给银行3.8%的贷款总额当利息&#xff0c;对吧&#xff1f;但别急&#xff0c;这里头学问大着呢&#xff01;有时候&#xff0c;你发现标着3.8%的…

keil调试变量值被篡改问题

今天遇到一个代码中变量值被篡改的问题&#xff0c;某个数组的第一个值运行一段时间之后变成了0&#xff0c;如图&#xff1a; 看现象基本可以断定是内存越界导致的&#xff0c;但是要如果定位是哪里内存越界呢? keil提供了两个工具 1、set access breakpoint at(设置访问断点…

ES6标准---【八】【学习ES6看这一篇就够了!!!】

目录 前言 export命令 输出变量 输出函数/类 export中的as别名 export必须一一对应 export接口的响应性 注意 import命令 import命令的语法 import命令里的as别名 import的只读性 import命令具有提升性 import的一些约定 import的静态执行 import的唯一执行性 模…

基于SmartUpload组件实现文件上传功能的案例

SmartUpload组件简介 SmartUpload组件 专门用于实现文件上传及下载的免费组件SmartUpload组件特点 使用简单&#xff1a;编写少量代码&#xff0c;完成上传下载功能能够控制上传内容能够控制上传文件的大小、类型缺点&#xff1a;目前已停止更新服务 SmartUpload组件应用 单文…