【C++】 函数模板和类模板

文章目录

  • 一、模板
    • 1.1 函数模板和类模板
    • 1.2 函数模板
      • 1.2.1 普通函数和函数模板区别
      • 1.2.2 普通函数和函数模板调用规则
      • 1.2.3 模板局限性
    • 1.3 类模板
      • 1.3.1 类模板对象做函数参数
      • 1.3.2 类模板的继承
      • 1.3.3 类模板成员函数的类外实现
      • 1.3.4 类模板分文件编写
      • 1.3.5 类模板全局函数类内实现

  本文是我在学习C++过程当中的心得和学习笔记,在学习C++时已经有C语言的基础,因此入门知识省略了一部分。文章包含了C++的入门基础内容和核心进阶内容,并附上了学习的代码,仅供大家参考。如果有问题,有错误欢迎大家留言。剩余的内容可以通过 这篇文章找到。

一、模板

1.1 函数模板和类模板

  函数模板:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
  模板的目的是为了提高复用性,将类型参数化。

/*模板语法:
template<typename T>
函数声明或定义
*/
/*template 声明创建模板,typename表明其后面的符号是一种数据类型,可以用class代替,T表示通用的数据类型,名称可以替换,通常为答谢字母*/
template<typename T>
void myswap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}
void test2()
{
	int a = 10;		// 将int换成其他类型也可以运行
	int b = 20;
	//myswap(a, b);	// 自动类型推导
	myswap<int>(a, b);// 显示指定类型
	/*两种方式使用模板:1.自动类型推导 2.显示指定类型*/
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}

  数组排序:

template<typename T>
void myswap(T& a, T& b)	// 交换函数通用模板
{
	T temp = a;
	a = b;
	b = temp;
}
template<class T>
void myArrsort(T arr[], int len)	// 数组选择排序通用模板
{
	for (int i = 0; i < len; i++)
	{
		int max = i;	// 认定最大值下标
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;	// 更新最大值下标
			}
		}
		if (max != i)
		{
			myswap(arr[max], arr[i]);
		}
	}
}
template<class T>
void printArr(T arr[], int len)	// 数组打印通用模板
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
}
void test1()
{
	char charArr[] = "eafcbd";
	int num = sizeof(charArr) / sizeof(char);
	myArrsort(charArr, num);
	printArr(charArr, num);
}
void test2()
{
	int intArr[] = { 8,4,5,6,3,9,2,1 };
	int num = sizeof(intArr) / sizeof(int);
	myArrsort(intArr, num);
	printArr(intArr, num);
}

1.2 函数模板

1.2.1 普通函数和函数模板区别

int myAdd1(int a, int b)
{
	return a + b;
}
template<class T>
T myAdd2(T a, T b)	// 交换函数通用模板
{
	return a + b;
}

void test1()
{
	int a = 10;
	int b = 20;
	char c = 'c';	// a-97  c-99
	cout << myAdd1(a, c) << endl;	// c转换成ASCII码相加
	// 自动类型推导 报错, 不会发送隐式类型转换
	//cout << myAdd2(a, c) << endl;	
	// 显示指定int类型,会发生隐式类型转换
	cout << myAdd2<int>(a, c) << endl;
}

1.2.2 普通函数和函数模板调用规则

在这里插入图片描述

void myPrint(int a, int b)
{
	cout << "调用普通函数" << endl;
}
template<class T>
void myPrint(T a, T b)
{
	cout << "调用模板" << endl;
}
template<class T>
void myPrint(T a, T b, T c)
{
	cout << "调用重载模板" << endl;
}
void test1()
{
	int a = 10;
	int b = 20;
	myPrint(a, b);	// 调用的普通函数
	myPrint<>(a, b);	// 通过空模板参数列表,强制调用函数模板
	myPrint(a, b, 100);	// 模板也可以有重载
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2);	// 模板的类型更匹配,调用的模板
}

1.2.3 模板局限性

  模板并不是万能的,某些特定的数据类型,需要用具体化的方式做实现。

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;
}
template<class T>
void myPrint(T a, T b, T c)
{
	cout << "调用重载模板" << endl;
}

void test1()
{
	int a = 10;
	int b = 20;
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a == b " << endl;
	}
	else
	{
		cout << "a != b " << endl;
	}
}
void test2()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2 " << endl;
	}
	else
	{
		cout << "p1 != p2 " << endl;
	}
}

1.3 类模板

在这里插入图片描述
类模板和函数模板区别:
  1.类模板没有自动类型推导
  2.类模板在模板参数列表中可以有默认参数

template<class Nametype, class Agetype = int>  // 类型默认参数为string和int
class Person
{
public:
	Person(Nametype name, Agetype age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	Nametype m_name;
	Agetype m_age;
	void showPerson()
	{
		cout << "name: " << this->m_name << endl << "age: " << this->m_age << endl;
	}
};
void test1()
{
	// Person p1("张三", 18);	// 报错,没有自动类型推导
	Person<string, int> p1("张三",18);
	Person<string > p2("张三", 18);	// 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
{
	T obj;
public:
	void func1()
	{
		obj.showPerson1();	// 直接运行不报错,编译通过,这时类模板的成员函数还没有被创建
	}
	void func2()
	{
		obj.showPerson2();
	}
};
void test1()
{
	MyClass<Person1> m;
	m.func1();
	//m.func2();
}
int main()
{
	test1();
	system("pause");
	return 0;
}

1.3.1 类模板对象做函数参数

类模板的函数传参一般来讲,有以下三种方式:
  1、指定传入类型
  2、参数模板化
  3、整个类模板化

// 类模板中成员函数在调用时才去创建
class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};
class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show" << endl;
	}
};
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 << "年龄:" << this->m_age << endl;
	}
	T1 m_name;
	T2 m_age;
};
void printPerson1(Person<string, int>& p)	// 1、指定传入类型
{
	p.showPerson();
}
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p)	// 2、参数模板化
{
	p.showPerson();
	cout << "T1的类型为: " << typeid(T1).name() << endl;
	cout << "T2的类型为: " << typeid(T2).name() << endl;
}
template <class T>
void printPerson3(T& p)	// 3、整个类模板化
{
	p.showPerson();
	cout << "T的类型为: " << typeid(T).name() << endl;
}
void test1()
{
	Person<string, int>p1("张三 ", 18);
	Person<string, int>p2("李四 ", 25);
	Person<string, int>p3("王五 ", 30);
	printPerson1(p1);
	printPerson2(p2);
	printPerson3(p3);
}

1.3.2 类模板的继承

// 类模板与继承
template <class T>
class Base
{
	T m;
};
class Son : public Base<int> // 报错,必须要知道父类中T的类型,才能继承给子类,需要加上<int>、或者<char>
{

};
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 obj;
};
void test1()
{
	Son2<int, char>S2;	// int指定给T1,char指定给父类继承来的T2,在父类中表示为T
}

1.3.3 类模板成员函数的类外实现

// 类模板成员函数的类外实现
template <class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	void showPerson();
	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;
}
void test1()
{
	Person <string, int>P("Tom", 20);
	P.showPerson();
}

1.3.4 类模板分文件编写

在这里插入图片描述
  若仅包含.h文件,编译器在编译时是不会创建类模板的成员函数Person和showPerson,因此出现无法解析的错误(编译器不知道这两个函数是什么),若包含.cpp文件则可以运行,编译器会找着.cpp文件中包含的Person.h文件,最终两个文件都会包含。

// main.cpp文件
# include <iostream>
# include <string>
//#include<Person.h> // 编译器报错
//# include "Person.cpp" // 第一种解决方式,直接包含源文件
# include "Person.hpp" // 第二种解决方式,将.h和.cpp中的内容写到到一起,将后缀名改为.hpp
using namespace std;
// 类模板分文件编写
void test1()
{
	Person <string, int>P("Tom", 20);
	P.showPerson();
}
int main()
{
	test1();
	system("pause");
	return 0;
}
// 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文件
# include <iostream>
# include <string>
# include "Person.h"
using namespace std;
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;
}
// Person.hpp文件 = Person.h文件 + Person.cpp文件
#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;
};
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;
}

1.3.5 类模板全局函数类内实现

  不建议使用全局函数类外实现,过于复杂,类内实现用法简单,而且编译器可以直接识别

// 提前让编译器知道Person类存在
template <class T1, class T2>
class Person;
// 类外实现
template <class T1, class T2>
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;
};
void test1()
{
	Person <string, int>P("Tom", 20);
	printPerson(P);
	printPerson2(P);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Cookie 和 Session 区别——2023最新面试精简版本

Cookie 和 Session 的区别 原理&#xff1a;从”登录“过程看Jwt和Token&#xff0c;以及区分Cookie和Session概念 面试&#xff1a; 好的&#xff0c;面试官。 我先解释一下 Cookie&#xff0c;它是客户端浏览器用来保存服务端数据的一种机制。 当通过浏览器进行网页访问的时…

echarts坐标轴名称换行

一、期望效果&#xff1a; 期望超过6个字换行&#xff0c;最多可显示十个字 如图&#xff1a; 二、踩坑&#xff1a; echarts的width和overflow设置后换行无效。&#xff08;如果其他人有设置有效的 还请说明下&#xff09; 三、解决方案&#xff1a; 用\n换行&#xf…

CSS :nth-child

CSS :nth-child :nth-child 伪类根据元素在同级元素中的位置来匹配元素. CSS :nth-child 语法 值是关键词 odd/evenAnB最新的 [of S] 语法权重 浏览器兼容性 很简单的例子, 来直觉上理解这个伪类的意思 <ul><li class"me">Apple</li><li>B…

14.Netty源码之模拟简单的HTTP服务器

highlight: arduino-light 简单的 HTTP 服务器 HTTP 服务器是我们平时最常用的工具之一。同传统 Web 容器 Tomcat、Jetty 一样&#xff0c;Netty 也可以方便地开发一个 HTTP 服务器。我从一个简单的 HTTP 服务器开始&#xff0c;通过程序示例为你展现 Netty 程序如何配置启动&a…

智能视频监控平台EasyCVR电子地图视频播放全屏情况下的异常排查与解决

安防视频监控平台TSINGSEE青犀视频EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。 在视频监控管理平台TSINGSEE青…

Vue项目中强制刷新页面的方法

我们在动态切换组件的过程中&#xff0c;导航栏和底栏不动&#xff0c;动态切换中间区域的情况&#xff0c;在首页可以进行跳转任意组件&#xff0c;在组件与组件之间不能相互跳转&#xff0c;路由发生了变化&#xff0c;但是页面未改变&#xff0c;这时我们就需要强制刷新页面…

Individual household electric power consumption个人家庭用电量数据挖掘与时序预测建模

今天接到一个任务就是需要基于给定的数据集来进行数据挖掘分析相关的计算&#xff0c;并完成对未来时段内数据的预测建模&#xff0c;话不多少直接看内容。 官方数据详情介绍在这里&#xff0c;如下所示&#xff1a; 数据集中一共包含9个不同的字段&#xff0c;详情如下&#…

AP5216 DC-DC降恒流驱动IC LED电动摩托汽车 转向灯刹车灯雾灯驱动

产品描述 AP5216 是一款 PWM工作模式, 高效率、外围简单、内置功率管&#xff0c;适用于5V&#xff5e;100V输入的高精度降压 LED 恒流驱动芯片。输出最大功率可达9W&#xff0c;最大电流 1.0A。AP5216 可实现全亮/半亮功能切换&#xff0c;通过MODE 切换&#xff1a;全亮/半亮…

序列化模块pickle和json有什么区别

目录 什么是序列化模块pickle 什么是序列化模块json pickle和json有什么区别 总结 什么是序列化模块pickle pickle是Python中的内置模块&#xff0c;用于将Python对象序列化和反序列化为字节流。它提供了一种将复杂的数据结构&#xff08;如列表、字典、类实例等&#xff0…

字典树Trie

Trie树又称字典树&#xff0c;前缀树。是一种可以高效查询前缀字符串的树&#xff0c;典型应用是用于统计&#xff0c;排序和保存大量的字符串&#xff08;但不仅限于字符串&#xff09;&#xff0c;所以经常被搜索引擎系统用于文本词频统计。 它的优点是&#xff1a;利用字符串…

【图像处理】使用 OpenCV 将您的照片变成卡通

图像到卡通 一、说明 在当今世界&#xff0c;我们被图像和视频所包围。从社交媒体到广告&#xff0c;图像已成为一种强大的交流媒介。但是你有没有想过&#xff0c;如果你能把你的照片变成卡通会发生什么&#xff1f;想象一下&#xff0c;为您最喜欢的照片创建动画版本&#xf…

CSP 2021入门级 第一轮 题目讲解

A: a进栈&#xff0c;直接出栈&#xff1b;b进栈&#xff0c;直接出栈&#xff1b;c进栈&#xff0c;直接出栈&#xff1b;d进栈&#xff0c;直接出栈&#xff1b;e进栈&#xff0c;直接出栈。 B&#xff1a;全进栈后全出栈。 C&#xff1a;a和b先进栈&#xff0c;然后直接出…

网络安全(黑客)自学建议笔记

前言 网络安全&#xff0c;顾名思义&#xff0c;无安全&#xff0c;不网络。现如今&#xff0c;安全行业飞速发展&#xff0c;我们呼吁专业化的 就职人员与大学生 &#xff0c;而你&#xff0c;认为自己有资格当黑客吗&#xff1f; 本文面向所有信息安全领域的初学者和从业人员…

Spring AOP (面向切面编程)原理与代理模式—实例演示

一、AOP介绍和应用场景 Spring 中文文档 (springdoc.cn) Spring | Home 官网 1、AOP介绍&#xff08;为什么会出现AOP &#xff1f;&#xff09; Java是一个面向对象&#xff08;OOP&#xff09;的语言&#xff0c;但它有一些弊端。虽然使用OOP可以通过组合或继承的方…

使用xtcp映射穿透指定服务

使用xtcp映射穿透指定服务 管理员Ubuntu配置公网服务端frps配置service自启(可选) 配置内网服务端frpc配置service自启(可选) 使用者配置service自启(可选) 通过frp实现内网client访问另外一个内网服务器 管理员 1&#xff09;配置公网服务端frps2&#xff09;配置内网服务端…

ARM汇编中预定义的寄存器和协处理器名称

一、是什么? 预定义的寄存器和协处理器名称,汇编代码中直接使用就可以. # 二、使用步骤 1.引入库 代码如下(示例): .global _start _start:mov r0,#0x18LDR R3,=0x55555555mov r1,#0x18LDR R1,=0x55555555mov r2,#

java对象的强引用,弱引用,软引用,虚引用

前言:java对象在java虚拟机中的生存状态&#xff0c;面试可能会有人问道&#xff0c;了解一下 这里大量引用 《疯狂Java讲义第4版》 书中的内容

⛳ 面向对象面试题

面向对象面试题目录 ⛳ 面向对象面试题&#x1f69c; 一&#xff0c;成员变量&#xff0c;局部变量&#xff0c;类变量存储在内存的什么地方&#xff1f;&#x1f43e; 1.1&#xff0c;类变量&#xff08;静态成员变量&#xff09;&#x1f4dd; 1.2&#xff0c;成员变量⭐ 1.3…

Redis(四)—— Redis基本的事务操作、Redis实现乐观锁

一、Redis基本的事务操作 首先声明&#xff1a; redis的单条命令是保证原子性的&#xff08;回想一下setnx k1 v1 k5 v5命令如果k1已经存在&#xff0c;那么k5也会设置失败&#xff09;但是redis的事务不保证原子性&#xff01;见下面“1.2 某条命令有错怎么办&#xff1f;”…

Ueditor 百度强大富文本Springboot 项目集成使用(包含上传文件和上传图片的功能使用)简单易懂,举一反三

Ueditor 百度强大富文本Springboot 项目集成使用 首先如果大家的富文本中不考虑图片或者附件的情况下&#xff0c;只考虑纯文本且排版的情况下我们可以直接让前端的vue来继承UEditor就可以啦。但是要让前端将那几个上传图片和附件的哪些功能给阉割掉&#xff01; 然后就是说如…