当前位置: 首页 > news >正文

快速入门C++第六天——函数模板与类模板

  • 该系列文章大部分摘自博主白鳯《C++面向对象程序设计》✍千处细节、万字总结(建议收藏)http://t.csdn.cn/GxZ6U
  • 如有不好的影响请联系删除

模板的概念

在程序设计中往往存在这样的现象:两个或多个函数的函数体完全相同,差别仅在与它们的参数类型不同。

int Max(int x, int y) {
    return x >= y ? x : y;
}

double Max(double x, double y) {
    return x >= y ? x : y;
}

能否为上述这些函数只写出一套代码呢?解决这个问题的一种方式是使用宏定义

#define Max(x, y)((x >= y) ? x : y)

宏定义带来的另一个问题是,可能在不该替换的地方进行了替换,而造成错误。事实上,由于宏定义会造成不少麻烦,所以在C++中不主张使用宏定义。解决以上问题的另一个方法就是使用模板。

函数模板

所谓函数模板,实际上是建立一个通用函数,其函数返回类型和形参类型不具体指定,用一个虚拟的类型来代表,这个通用函数就称为函数模板。在调用函数时,系统会根据实参的类型(模板实参)来取代模板中的虚拟类型,从而实现不同函数的功能。

template <typename 类型参数>
返回类型 函数名(模板形参表)
{
    函数体
}
也可以定义为如下形式
template <class 类型参数>
返回类型 函数名(模板形参表)
{
    函数体
}

实际上,template是一个声明模板的关键字,它表示声明一个模板。类型参数(通常用C++标识符表示,如T、type等)实际上是一个虚拟的类型名,使用前并未指定它是哪一种具体的类型,但使用函数模板时,必须将类型实例化。类型参数前需加关键字typename或class,typename和class的作用相同,都是表示一个虚拟的类型名(即类型参数)。

例1:一个与指针有关的函数模板

#include <iostream>
using namespace std;

template <typename T>
T Max(T *array, int size = 0) {
	T max = array[0];
	for (int i = 1	; i < size; i++) {
		if (array[i] > max) max = array[i];
	}
	return max;
}

int main() {
	int array_int[] = {783, 78, 234, 34, 90, 1};
	double array_double[] = {99.02, 21.9, 23.90, 12.89, 1.09, 34.9};
	int imax = Max(array_int, 6);
	double dmax = Max(array_double, 6);
	cout << "整型数组的最大值是:" << imax << endl;
	cout << "双精度型数组的最大值是:" << dmax << endl;
	return 0;
}

例2:函数模板的重载

#include <iostream>
using namespace std;

template <class Type>
Type Max(Type x, Type y) {
	return x > y ? x : y;
}

template <class Type>
Type Max(Type x, Type y, Type z) {
	Type t = x > y ? x : y;
	t = t > z ? t : z;
	return t;
}

int main() {
	cout << "33,66中最大值为 " << Max(33, 66) << endl;
	cout << "33,66,44中最大值为 " << Max(33, 66, 44) << endl;
	return 0;
}

注意:

  • 在函数模板中允许使用多个类型参数。但是,应当注意template定义部分的每个类型参数前必须有关键字typename或class。

  • 在template语句与函数模板定义语句之间不允许插入别的语句。

  • 同一般函数一样,函数模板也可以重载。

  • 函数模板与同名的非模板函数可以重载。在这种情况下,调用的顺序是:首先寻找一个参数完全匹配的非模板函数,如果找到了就调用它;若没有找到,则寻找函数模板,将其实例化,产生一个匹配的模板参数,若找到了,就调用它。

类模板

所谓类模板,实际上就是建立一个通用类,其数据成员、成员函数的返回类型和形参类型不具体指定,用一个虚拟的类型来代表。使用类模板定义对象时,系统会根据实参的类型来取代类模板中虚拟类型,从而实现不同类的功能。

template <typename T>
class Three{
private:
    T x, y, z;
public:
    Three(T a, T b, T c) {
        x = a; y = b; z = c;
    }
    T sum() {
        return x + y + z;
    }
}

上面的例子中,成员函数(其中含有类型参数)是定义在类体内的。但是,类模板中的成员函数也可以在类模板体外定义。此时,若成员函数中有类型参数存在,则C++有一些特殊的规定:

  1. 需要在成员函数定义之前进行模板声明;
  2. 在成员函数名前要加上“类名<类型参数>::”;

在类模板体外定义的成员函数的一般形式如下:

template <typename 类型参数>
函数类型 类名<类型参数>::成员函数名(形参表)
{
    ·····
}

例如,上例中成员函数sum()在类外定义时,应该写成

template<typename T>
T Three<T>::sum() {
    return x + y + z;
}

例子:栈类模板的使用

#include <iostream>
#include <string>
using namespace std;

const int size = 10;
template <class T>
class Stack{
private:
	T stack[size];
	int top;
public:
	void init() {
		top = 0;
	}
	void push(T t);
	T pop();
};

template <class T>
void Stack<T>::push(T t) {
	if (top == size) {
		cout << "Stack is full!" << endl;
		return;
	}
	stack[top++] = t;
}

template <class T>
T Stack<T>::pop() {
	if (top == 0) {
		cout << "Stack is empty!" <<endl;
		return 0;
	}
	return stack[--top];
}

int main() {
	Stack<string> st;
	st.init();
	st.push("aaa");
	st.push("bbb");
	cout << st.pop() << endl;
	cout << st.pop() << endl;

	return 0;
}

相关文章:

  • gitlab自定义头像设置
  • 新库上线 | CnOpenData采矿业工商注册企业基本信息数据
  • 【Redis】基于Redis6的数据类型以及相关命令、应用场景整理
  • Qt使用qBreakpad定位崩溃位置
  • IAR+vscode开发环境搭建,千万别用,当心爱上
  • 一些 Next Generation ABAP Platform 的新语法用例
  • java面向对象思维程序设计开发以及案例 -电梯运行问题对象分析与程序设计(1)
  • vulnhub EMPIRE: BREAKOUT靶机
  • 【Python】PyQt5 Designer工具配置
  • Camera-MTK OpenCamera时序以及耗时
  • SpringCloud链路追踪SkyWalking-第二章-部署搭建及高可用
  • springboot vue3 elementui plus点餐外卖系统源码
  • Node.js阶段学习(一)
  • 一、nacos安装与高可用部署
  • mysql实现删除某一列的重复数据(只留一行或全部删除)
  • [PHP内核探索]PHP中的哈希表
  • 《Java编程思想》读书笔记-对象导论
  • 2017前端实习生面试总结
  • css选择器
  • django开发-定时任务的使用
  • docker-consul
  • export和import的用法总结
  • Node 版本管理
  • 订阅Forge Viewer所有的事件
  • 关于extract.autodesk.io的一些说明
  • 深入浅出Node.js
  • 数据可视化之 Sankey 桑基图的实现
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 为什么要用IPython/Jupyter?
  • 线上 python http server profile 实践
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • !!java web学习笔记(一到五)
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (C语言)二分查找 超详细
  • .bat批处理(一):@echo off
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • .NET中的十进制浮点类型,徐汇区网站设计
  • ?.的用法
  • @ConfigurationProperties注解对数据的自动封装
  • @html.ActionLink的几种参数格式
  • @synthesize和@dynamic分别有什么作用?
  • [ 环境搭建篇 ] 安装 java 环境并配置环境变量(附 JDK1.8 安装包)
  • [Angular] 笔记 8:list/detail 页面以及@Input
  • [datastore@cyberfear.com].Elbie、[thekeyishere@cock.li].Elbie勒索病毒数据怎么处理|数据解密恢复
  • [Django 0-1] Core.Email 模块
  • [emacs] CUA的矩形块操作很给力啊
  • [EULAR文摘] 利用蛋白组学技术开发一项蛋白评分用于预测TNFi疗效
  • [Google Guava] 1.1-使用和避免null
  • [JDK工具-2] javap 类文件解析工具-帮助理解class文件,了解Java编译器机制
  • [jQuery]使用jQuery.Validate进行客户端验证(中级篇-上)——不使用微软验证控件的理由...
  • [leveldb] 2.open操作介绍