C++基础——模板

文章目录

  • 1 概述
  • 2 函数模板
    • 2.1 使用函数模板
    • 2.2 函数模板注意事项
    • 2.3 普通函数和函数模板的区别
    • 2.4 普通函数与函数模板的调用规则
    • 2.5 模板的局限性
  • 2 类模板
    • 2.1 类模板语法
    • 2.2 类模板和函数模板的区别
    • 2.3 类模板中成员函数的创建时机
    • 2.4 类模板与继承
    • 2.5 类模板成员函数类外实现
    • 2.6 类模板份文件编写
    • 2.7 类模板与友元

1 概述

模板是一种较为通用的模具,不能单独使用
C++除了面向对象编程之外,还有一种是泛型编程,使用的就是模板
C++中提供了两种模板:函数模板,类模板

2 函数模板

2.1 使用函数模板

函数模板的作用是建立一个通用的函数,其返回值类型和形参类型可以不具体定制,用一个虚拟的类型来代表
语法:

template<typename T>
函数

template – 声明创建模板
typename – 表明其后面的符号是一种数据类型,可以用class代替
T – 通用数据类型,名称可以替换

#include <iostream>using namespace std;template<typename T>
void mySwap(T &a, T &b) {T temp = a;a = b;b = temp;
}int main() {int a = 10;int b = 20;mySwap(a, b);cout << "a = " << a << endl;cout << "b = " << b << endl;string c = "111";string d = "222";mySwap<string>(c, d);cout << "c = " << c << endl;cout << "d = " << d << endl;return 0;
}

使用模板有两种方式,一种是自动推导类型,一种是显示指定类型
这里使用模板,可以传入多种类型的参数,当int类型参数传入的时候,T被推导成int类型,当string类型参数传入的时候,T被推导成string类型。

2.2 函数模板注意事项

函数模板定义于函数前,只能用于其后的第一函数,且函数模板必须推导出一致的类型

int a = 10;
char b = 'a';
mySwap(a, b);

编译失败,因为推导不出一致的类型

template<typename T>
void func() {cout << "func" << endl;
}int main() {
//    func();func<int>();
}

必须推导出具体的类型,函数模板才能够正常调用,否则编译失败

2.3 普通函数和函数模板的区别

普通函数可以发生自动类型转换,函数模板如果使用自动类型推导,则不会发生类型的隐式转换。如果函数模板使用显示类型,则可以发生隐式转换

template<typename T>
int addNum(T a, T b) {return a + b;
}int main() {int a = 10;int b = 20;addNum(a, b);//addNum(a, c);char c = 'a';addNum<int>(a, c);
}

2.4 普通函数与函数模板的调用规则

  • 1.如果函数模板和普通函数都可以实现,优先调用普通函数
  • 2.可以通过空模板参数列表来强制调用函数模板
  • 3.函数模板也可以发生重载
  • 4.如果函数模板可以产生更好的匹配,优先调用函数模板
#include <iostream>using namespace std;void print(int a, int b) {cout << "普通函数" << endl;
}template<typename T>
void print(T a, T b) {cout << "函数模板" << endl;
}template<typename T>
void print(T a, T b, T c) {cout << "重载的函数模板" << endl;
}int main() {int a = 10;int b = 20;// 调用普通函数print(a,b);// 调用函数模板print<>(a,b);// 调用重载的函数模板int c = 30;print(a, b, c);// 调用函数模板char d = 'a';char e = 'b';print(d, e);
}

实践中,如果提供了函数模板就没有必要提供普通函数了,以免造成二义性

2.5 模板的局限性

模板并不是完成的

template<typename T>
void f(T a, T b) {a = b;
}

对于上述的赋值操作,如果传入的参数是数组,则无法实现

template<class T>
void f(T a, T b) {if (a > b) { ... }
}

如果传入的参数是自定义的类对象,则上面的模板也无法运行
对于上面的这种问题,C++提供了一种函数模板的重载方式,可以为这些特定的类型提供具象化的模板。

#include <iostream>using namespace std;class Person {
public:Person(string name, int age) {this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};template<class T>
bool myCompare(T &a, T &b) {if (a == b) {return true;} else {return false;}
}// 提供具象化的模板函数重载
template<>
bool myCompare(Person &p1, Person &p2) {if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age) {return true;} else {return false;}
}int main() {int a = 10;int b = 10;cout << myCompare(a, b) << endl;Person p1("zhangsan", 23);Person p2("lisi", 23);cout << myCompare(p1, p2) << endl;return 0;
}

使用具体化模板,可以解决自定义类型的通用化

2 类模板

2.1 类模板语法

类模板的作用:创建一个通用的类,类中成员变量的类型可以不具体指定,用虚拟类型来代表
语法:

template<typename T>

示例:

#include <iostream>using namespace std;template<class NameType, class AgeType>
class Person {
public:Person(NameType name, AgeType age) {m_Name = name;m_Age = age;}void showPerson() {cout << "name = " << m_Name << " , age = " << m_Age << endl;}NameType m_Name;AgeType m_Age;
};int main() {Person<string, int> person("zhangsan", 21);person.showPerson();return 0;
}

创建了一个类模板,在创建对象的时候,可以通过显示的限定数据类型来让模板中的通用类型拥有具体的类型,也可以让编译器自动进行推导

Person person("zhangsan", 21);

2.2 类模板和函数模板的区别

类模板在模板参数定义的时候可以有默认值

#include <iostream>using namespace std;template<class NameType, class AgeType = int>
class Person {
public:Person(NameType name, AgeType age) {m_Name = name;m_Age = age;}void showPerson() {cout << "name = " << m_Name << " , age = " << m_Age << endl;}NameType m_Name;AgeType m_Age;
};int main() {Person<string> person("zhangsan", 20);person.showPerson();return 0;
}

设置模板默认值,在使用模板类时,可以不需要限定默认参数的类型
对于默认参数类型,可以在显示限定的时候覆盖默认类型

Person<string, double> person("zhangsan", 22.3);

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

类模板中成员函数创建是在调用时才创建的

#include <iostream>using namespace std;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 fun1() { obj.showPerson1(); }void fun2() { obj.showPerson2(); }
};int main() {MyClass<Person1> m;m.fun1();// m.fun2();//编译会出错,说明函数调用才会去创建成员函数return 0;
}

当不调用fun2的时候,代码能够编译通过,调用的时候就会报错,因为调用的时候才会识别到T被初始化为Person1类型,而Person2中没有showPerson2函数。

2.4 类模板与继承

类模板与继承相关时,需要注意

  • 父类如果存在类模板,子类在声明的时候需要指定父类中的模板类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定父类中T的类型,子类需变为类模板
#include <iostream>using namespace std;template<class T>
class Base
{T m;
};// class Son:public Base  //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
{
};
void test01()
{Son c;
}//类模板继承类模板 ,可以用T2指定父类中的T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:Son2(){cout << typeid(T1).name() << endl;cout << typeid(T2).name() << endl;}
};void test02()
{Son2<int, char> child1;
}int main() {test01();test02();return 0;
}

继承类模板,必须显示指定其类型,如果不显示指定,可以将需要指定父类的类型也声明成一个模板,在子类构造的时候传入。

2.5 类模板成员函数类外实现

#include <iostream>
#include <string>using namespace std;//类模板中成员函数类外实现
template<class T1, class T2>
class Person {
public://成员函数类内声明Person(T1 name, T2 age);void showPerson();public:T1 m_Name;T2 m_Age;
};//构造函数 类外实现
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>
void Person<T1, T2>::showPerson() {cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}int main() {Person<string, int> p("Tom", 20);p.showPerson();return 0;
}

类模板成员函数的类外实现,除了需要重新声明模板外还需要限定他是一个类模板(使用<>)

2.6 类模板份文件编写

问题

  • 类模板中成员函数创建时机在调用阶段,导致份文件编写时链接不到
    解决
  • 直接包含.cpp文件
  • 将声明和实现放到一个文件中,并更改后缀为.hpp,hpp为约定名

推荐使用第二种方式

#ifndef STLDEMO1_PERSON_HPP
#define STLDEMO1_PERSON_HPP#include<iostream>using namespace std;template<class T1, class T2>
class Person {
public://成员函数类内声明Person(T1 name, T2 age);void showPerson();public:T1 m_Name;T2 m_Age;
};//构造函数 类外实现
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>
void Person<T1, T2>::showPerson() {cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}#endif

创建hpp文件,将声明和实现放到一个文件中进行导入。因为分文件导入,调用的时候才创建函数,最开始的时候只包含了头文件,没有生成对应的函数,在调用的时候才生成,所以找不到函数。

2.7 类模板与友元

可以分为两种,一种是全局函数类内实现,一种是全局函数类外实现
1、全局函数类内实现

#include <iostream>
#include <string>using namespace std;template<class T1, class T2>
class Person {//1、全局函数配合友元   类内实现friend void printPerson(Person<T1, T2> &p) {cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;}public:Person(T1 name, T2 age) {this->m_Name = name;this->m_Age = age;}private:T1 m_Name;T2 m_Age;
};int main() {Person<string, int> p("Jerry", 30);printPerson(p);return 0;
}

直接类内实现,并调用
类外实现较为复杂一些

#include <iostream>
#include <string>using namespace std;template<class T1, class T2>
class Person;template<class T1, class T2>
void printPerson(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);
public:Person(T1 name, T2 age) {this->m_Name = name;this->m_Age = age;}private:T1 m_Name;T2 m_Age;
};int main() {Person<string, int> p("Jerry", 30);printPerson(p);return 0;
}

友元定义的时候需要加<>让编译器识别该友元函数是一个模板函数。否则编译失败,其他的调整就是让编译器能够识别到对应函数或类,能够编译通过即可。

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

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

相关文章

C++17那些事开篇之类模版参数推导(CTAD)

C17那些事开篇之类模版参数推导(CTAD) 引入 大家好&#xff0c;我是光城&#xff0c;今天开始正式开篇C17的新特性了&#xff0c;期待不&#xff0c;欢迎留言区说出想要更新的特性呀&#xff5e; C模板元编程一直是C开发者们熟知的一项功能&#xff0c;无论是初学者还是高级开发…

【猜数字游戏】用wxPython实现:基本的游戏框架 + 简单的图形用户界面

【猜数字游戏】 写在最前面猜数字游戏 实现【猜数字游戏】安装wxPython全部代码代码解析1. 初始化界面2. 生成随机数3. 处理猜测4. 特殊功能5. 分数计算 游戏小程序呈现结语 写在最前面 看到了一个比较有意思的问题 https://ask.csdn.net/questions/8038039 猜数字游戏 在这…

苹果TF签名全称TestFlight签名,需要怎么做才可以上架呢?

如果你正在开发一个iOS应用并准备进行内测&#xff0c;TestFlight是苹果提供的一个免费的解决方案&#xff0c;它使开发者可以邀请用户参加应用的测试。以下是一步步的指南&#xff0c;教你如何利用TestFlight进行内测以便于应用后续可以顺利上架App Store。 1: 准备工作 在测…

ceph编译

1.执行./install-deps.sh提示deb无法安装 解决方案&#xff1a; 直接使用dpkg -i来安装deb&#xff0c;会提示缺失3个库&#xff0c;库名如下&#xff1a; cython python dh-systemd 其中cython和python手动安装好后&#xff0c;也会依旧报错。此时修改debian/control文件&…

客餐书房一体布局,新中式风格禅意十足。福州中宅装饰,福州装修

你是否曾经遇到过这样的痛点&#xff1a;装修时不知道该选择什么样的风格&#xff0c;让家居空间显得既时尚又实用&#xff1f;如果你对此感到困惑&#xff0c;那么新中式风格可能正是你想要的选择&#xff01; 今天&#xff0c;我们将一起探讨一种别样的家居布局&#xff0c;它…

如何用量化交易“做空”来获取收益

最近的市场环境相当不好&#xff0c;今年一年都没有任何主线的模式情况下去交易。更多的都是题材热点聚焦&#xff0c;而且板块轮动过快。市场环境不好的情况下&#xff0c;我们如何通过“做空”来获取收益&#xff01;量化做空是指利用计算机模型和算法&#xff0c;通过分析市…

EasyExcel如何读取全部Sheet页数据方法

一、需求描述 Excel表格里面大约有20个sheet页&#xff0c;每个sheet页65535条数据&#xff0c;需要读取全部数据&#xff0c;并导入至数据库。 找了好多种方式&#xff0c;EasyExcel比较符合&#xff0c;下面看代码。 二、实现方式 采用EasyExcel框架的doReadAll()方法 1、…

python监控显卡显存

文章目录 内存监控进程监控获取进程pid管理进程pid的死活 内存监控 在工作组中&#xff0c;经常会遇到多人争抢显卡的情况&#xff0c;僧多肉少&#xff0c;所以可以做个监控显卡的头部程序&#xff0c;然后再调取主程序训练模型。 我们借用pynvml&#xff0c;没有安装的&…

【Redis】Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

目录 Redis的慢查询 慢查询配置 慢查询操作命令 慢查询建议 Pipeline 事务 Redis的事务原理 Redis的watch命令 Pipeline和事务的区别 Lua Lua入门 安装Lua Lua基本语法 注释 标示符 关键词 全局变量 Lua中的数据类型 Lua 中的函数 Lua 变量 Lua中的控制语句…

Unity DOTS《群体战斗弹幕游戏》核心技术分析之3D角色动画

最近DOTS发布了正式的版本, 我们来分享现在流行基于群体战斗的弹幕类游戏&#xff0c;实现的核心原理。今天给大家介绍大规模战斗群体3D角色的动画如何来实现。 DOTS 对角色动画支持的局限性 截止到Unity DOTS发布的版本1.0.16,目前还是无法很好的支持3D角色动画。在DOTS 的b…

蓝桥杯-02-蓝桥杯C/C++组考点与14届真题

文章目录 蓝桥杯C/C组考点与14届真题参考资源C/C组考点1. 组别2. 竞赛赛程3. 竞赛形式4. 参赛选手机器环境5. 试题形式5.1. 结果填空题5.2. 编程大题 6. 试题考查范围7. 答案提交8. 评分9. 样题样题 1&#xff1a;矩形切割&#xff08;结果填空题&#xff09;样题 2&#xff1a…

群晖NAS配置之搭建WordPress个人博客站点

群晖NAS配置之搭建WordPress个人博客站点 之前写了一些ngrok和frp给群晖nas做内网穿透&#xff0c;今天分享一下在群晖nas下安装wordpress的教程。 WordPress是一个开源的内容管理系统&#xff08;CMS&#xff09;&#xff0c;最初是用来搭建博客的&#xff0c;但后来发展成为…

中职组网络安全-linux渗透测试-Server2203(环境+解析)

任务环境说明&#xff1a; 服务器场景&#xff1a;Server2203&#xff08;关闭链接&#xff09; 用户名&#xff1a;hacker 密码&#xff1a;123456 1.使用渗透机对服务器信息收集&#xff0c;并将服务器中SSH服务端口号作为flag提交&#xff1b; FLAG:2232 2. 使用渗透机对…

单链表相关经典算法OJ题:移除链表元素

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 题目&#xff1a;移除链表元素 解法一&#xff1a; 解法一的代码实现&#xff1a; 解法二&#xff1a; 解法二代码的实现&#xff1a; 总结 前言 世上有两种耀眼的…

改进LiteOS中物理内存分配算法(详细实验步骤+相关源码解读)

一、实验要求 优化TLSF算法&#xff0c;将Best-fit策略优化为Good-fit策略&#xff0c;进一步降低时间复杂度至O(1)。 优化思路&#xff1a; 1.初始化时预先为每个索引中的内存块挂上若干空闲块&#xff0c;在实际分配时避免分割&#xff08;split&#xff09;操作&#xff…

JAVA实战演练之自动驾驶系统

一基本概念&#xff1a; 1. 传感器数据处理&#xff1a; 自动驾驶汽车需要收集大量的数据&#xff0c;包括来自雷达、激光雷达(LiDAR)、摄像头等传感器的数据。这些数据需要通过JAVA程序进行处理和解析&#xff0c;以便汽车能够理解其周围环境。 2. 控制算法&#xff1a; JAVA可…

TA-Lib学习研究笔记(三)——Volatility Indicator

TA-Lib学习研究笔记&#xff08;三&#xff09;——Volatility Indicator 波动率指标函数组 Volatility Indicators: [‘ATR’, ‘NATR’, ‘TRANGE’] 1.ATR Average True Range 函数名&#xff1a;ATR 名称&#xff1a;真实波动幅度均值 简介&#xff1a;真实波动幅度均值…

力扣labuladong——一刷day61

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣865. 具有所有最深节点的最小子树二、力扣1123. 最深叶节点的最近公共祖先三、力扣1026. 节点与其祖先之间的最大差值四、力扣1120. 子树的最大平均值 …

SQL行转列,将json数组拆分成多行,一行变多行例子,(LATERAL VIEW explode)

需求背景 有一张工程师能力表&#xff0c;如&#xff1a; 张三 擅长java、c、c、c#、js 李四 擅长java、go 王五 擅长css、f# 陈六 擅长as、c、java 。。。 需要统计擅长各种语言的工程师的数量&#xff0c;如&#xff1a; java 3 c 2 go 1 代码示例 使用LATERAL VIEW expl…

自建CA并生成自签名SSL证书

文章目录 前言一键生成自签名证书命令脚本执行结果 分步来看看自建证书的过程自建CA根证书生成CA私钥生成CA自签名证书 自签名SSL证书生成服务器私钥和证书申请文件CRS使用CA根证书签名SSL证书 最终的文件列表相关名词SSL/TLS协议证书颁发机构&#xff08;CA&#xff09;数字证…