系列文章目录

C plus plus ——【模板应用】


文章目录

  • 系列文章目录
  • 前言
  • 一、函数模板
    • 1.1、函数模板的定义
    • 1.2、函数模板的作用
    • 1.3、重载函数模板
  • 二、类模板
    • 2.1、类模板的定义与声明
    • 2.2、简单类模板
    • 2.3、默认模板参数
    • 2.4、为具体类型的参数提供默认值
  • 三、总结

前言

模板是C++语言的高级特性,分为函数模板和类模板两大类。模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现大大方便了大规模软件开发。


一、函数模板

函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。

1.1、函数模板的定义

函数模板定义的一般形式如下:

template <类型形式参数表>
返回类型  函数名(形式参数表)
{//函数体
}

template 为关键字,表示定义一个模板;尖括号“<>” 表示模板参数,模板参数主要有两种,一种是模型类型参数,另一种是模板非类型参数。模板类型参数使用关键字class 或 typedef 开始,其后是一个用户定义的合法标识符。模板非类型参数与普通参数定义相同,通常为一个常数。
可以将声明函数模板分成template部分和函数名部分。例如:

template<class T>
void fun(T t)
{//函数实现
}

定义一个求和的函数模板,例如:

template <class type>     //定义一个模板类型
type Sum(type xvar,type yvar)  //定义函数模板
{return xvar+yvar;
}

在定义完函数模板之后,需要在程序中调用函数模板。下面的代码演示了Sum函数的调用。

int  iret = Sum(10,20);          //实现两个整数的相加
double dret = Sum(10.5,20.5);    //实现两个实数的相加

如果采用如下的形式调用Sum函数模板,将会出现错误。

int  iret  = Sum(10.5,20);        //错误的调用
double dret = Sum(10,20.5);       //错误的调用

上述代码中为函数模板传递了两个类型不同的参数,编译器产生了歧义。如果用户在调用函数模块时显示标识模板类型,就不会出现错误了。例如:

int iret = Sum<int>(10.5,20);   //正确地调用函数模板
double dret = Sum<double>(10,20.5)  //正确地调用函数模板

用函数模板生成实际可执行的函数又称为模板函数。函数模板与模板函数不是一个概念。从本质上讲,函数模板是一个“框架”,它不是真正可以编译生成代码的程序,而模板函数是把函数模板中的类型参数实例化后生成的函数,它和普通函数本质是相同的,可以生成可执行代码。

1.2、函数模板的作用

假设求两个函数之中最大者,如果想求整型数和实型数需要定义以下两个函数:

int max(int a,int b)
{return a>b?a:b;  //返回最大值
}
float max(float a,float b)
{return a>b?a:b;  //返回最大值
}

可以使用函数模板以及#define 宏定义实现一个max 函数来完成既求整型数之间最大者又求实型数之间的最大者。
#define 宏定义可以在预编译期对代码进行替换。例如:

#define max(a,b) ((a)>(b)?(a):(b))

上述代码可以求整型数最大值和实型数最大值。但宏定义#define 只是进行简单替换,它无法对类型进行检查,有时计算结果可能不是预计的。例如:

#include <iostream>
#include <iomanip>
using namespace std;
#define max(a,b)((a)>(b)?(a):(b))
int main(int argc,char* argv[])
{int m=0,n=0;cout<<max(m,++n)<<endl;cout<<m<<setw(2)<<endl;  return 0;
}

程序运行结果如下:

程序运行的预期结果应该是1和0,但是实际是2和0,这是因为宏替换之后“++n”被执行了两次,因此n的值是2不是1。
宏是预编译指令,很难调试,无法单步进入宏的代码中。模板函数和#define 宏定义相似,但模板函数是用模板实例化得到的函数,它与普通函数没有本质区别,可以重载模板函数。
使用模板求最大值的代码如下:

template <class Type>
type max(Type a,Type b)
{if(a>b)return a;elsereturn b;
}

调用模板函数max可以正确计算整型数和实型数的最大值。例如:

cout<<"最大值:"<<max(10,1)<<endl;
cout<<"最大值:"<<max(200.05,100.4)<<endl;

使用数组作为模板参数。

#include <iostream>
using namespace std;
template <class type,int len>  //定义一个模板类型
type Max(type array[len])
{type ret = array[0];   //定义一个变量for(int i=1; i<len;i++)  //遍历数组元素{ret = (ret>array[i])?ret:array[i]; //比较数组元素大小}return ret;       //返回最大值
}
int main(int argc,char* argv[])
{int array[5] = {1,2,3,4,5};   //定义一个整型数组int iret = Max<int,5>(array);  //调用函数模板Maxcout<<iret<<endl;double dest[3] = {10.5,11.2,9.8}; //定义实数数组double dret = Max<double,3>(dest); //调用函数模板cout<<dret<<endl;
}

程序运行结果如下图所示:

程序中定义一个函数模板Max,用来求数组中元素的最大值,其中模板参数使用模板类型参数type和模板非类型参数len,参数type声明了数组中的元素类型,参数len声明了数组中的元素个数,给定数组元素后,程序将数组中的最大值输出。

1.3、重载函数模板

整型数和实型数编译器可以直接进行比较,所以使用函数模板后也可以直接进行比较。重载函数模板可以用来使字符指针指向的字符串进行比较。

#include <iostream>
#include <string>
using namespace std;
template <class Type>
Type Tmin(Type a,Type b)   //定义函数模板
{if(a<b)return a;elsereturn b;
}
char *Tmin(char *a,char *b)     //重载函数模板
{if(strcmp(a,b))return b;elsereturn a;
}
int main(int argc ,char* argv[])
{cout<<"Minimum:"<<min(10,1)<<endl;cout<<"Minimum:"<<min('a','b')<<endl;cout<<"Minimum:"<<min("hi","mr")<<endl;return 0;
}

程序运行结果如下图所示:

程序在重载的函数模板Tmin的实现,使用strcmp库函数来完成字符串的比较,此时使用Tmin函数可以比较整型数据、实型数据、字符数据和字符串数据。

二、类模板

使用template关键字不但可以定义函数模板,也可以定义类模板。类模板代表一族类,是用来描述通用数据类型或处理方法的机制,它使类中的一些数据成员和成员函数的参数或返回值可以取任意数据类型。类模板可以说是用类生成类,减少了类的定义数量。

2.1、类模板的定义与声明

类模板的一般定义形式如下:

template <类型形式参数表>
class   类模板名
{...//类模板体
};

类模板成员函数的定义如下:

template   <类型形式参数表>
返回类型   类模板名 <类型名表>::成员函数名(形式参数列表)
{... //函数体
}

template 是关键字,类型形式参数表与函数模板定义相同。类模板的成员函数定义时的类模板名与类模板定义时要一致,类模板不是一个真实的类,需要重新生成类,生成类的形式如下:

类模板名<类型实在参数表>

用新生成的类定义对象的形式如下:

类模板名<类型实在参数表>  对象名

其中类型实在参数表应与该类模板中的类型形式参数表匹配。用类模板生成的类称为模板类。类模板和模板类不是同一个概念,类模板是模板的定义,不是真实的类,定义中要用到类型参数;模板类本质上与普通的类相同,它是类模板的类型参数实例化之后得到的类。
定义一个容器的类模板,代码如下:

template <class Type>
class Container
{Type tltem;public:Container(){};void begin(const Type&tNew);void end(const Type&tNew);void insert(const Type&tNew);void empty(const Type&tNew);
};

和普通类一样,需要对类模板成员函数进行定义,代码如下:

void Container<type>::begin(const Type&tNew)     //容器的第一个元素
{tltem = tNew;
}
void Container<type>::end(const Type&tNew)  //容器的最后一个元素
{tltem = tNew;
}
void Container<type>::empty(const Type&tNew)  //清空容器
{tltem = tNew;
}

将模板类的参数设置为整型,然后用模板类声明对象,代码如下:

Container<int> myContainer;      //声明Container<int>类对象

声明对象后,就可以调用类成员函数,代码如下:

int i = 10;
myContainer.insert(i);

在类模板定义中,类型形式参数表中的参数也可以是其他类模板。例如:

template<template<class A> class B>
class CBase
{private:B<int> m_n;
}

类模板也可以进行继承。例如:

template<class T>
class CDerived public T
{public:CDrived();
};
template<class T>
CDerived<T>::CDerived:T()
{cout<<""<<endl;
}
void main()
{CDerived<CBase1>D1;
}

T是一个类,CDerived继承自该类,CDerived 可以对类T进行扩展。

2.2、简单类模板

类模板中的类型形式参数表可以在执行时指定,也可以在定义类模板时指定。简单类模板如下:
简单类模板。

#include<iostream>
using namespace std;template<class T1 ,class T2>
class MyTemplate
{T1  t1;T2  t2;
public:MyTemplate(T1 tt1,T2 tt2){t1 = tt1;t2 = tt2;}void display(){cout<<t1<<' '<<t2<<endl;}
};
int  main()
{int a=123;double b=3.1415;MyTemplate<int ,double>mt1(a,b);MyTemplate<int,int > mt2(a,b);mt1.display();mt2.display();return 0;}

程序运行结果如下图所示:

2.3、默认模板参数

默认模板参数是类模板中由默认的数据类型作参数,在模板定义时还可以为默认的数据类型声明变量,并且为变量赋值。为具体类型的参数提供默认值实例如下:

#include<iostream>
using namespace std;
template<class T1,class T2 = int>
class MyTemplate
{T1 t1;T2 t2;public:MyTemplate(T1 tt1,T2 tt2){t1 = tt1;t2 = tt2;}void display(){cout<<t1<<' '<<t2<<endl;}
};int main(int argc ,char* argv[])
{int a = 123;double b = 3.1415;MyTemplate<int ,double> mt1(a,b);MyTemplate<int> mt2(a,b);mt1.display();mt2.display();return 0;
}

程序运行结果如下图所示:

2.4、为具体类型的参数提供默认值

默认模板参数是类模板中由默认的数据类型作参数,在模板定义时还可以为默认的数据类型声明变量,并且为变量赋值。

#include<iostream>
using namespace std;
template<class T1,class T2,int num = 10>
class MyTemplate
{T1 t1;T2 t2;public:MyTemplate(T1 tt1,T2 tt2){t1 = tt1+num,t2 = tt2+num;}void display(){cout<<t1<<' '<<t2<<endl;}
};
int main(int argc ,char* argv[])
{int a = 123;double b = 3.1415;MyTemplate<int ,double> mt1(a,b);MyTemplate<int ,double,100> mt2(a,b);mt1.display();mt2.display();return 0;
}

程序运行结果如下图所示:

三、总结

模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现大大方便了大规模软件开发。

C plus plus ——【模板应用】相关推荐

  1. VS Code 安装插件、自定义模板、自定义配置参数、自定义主题、配置参数说明、常用的扩展插件

    1. 下载和官网教程 下载地址:https://code.visualstudio.com/ 官方教程:https://code.visualstudio.com/docs 2. 安装插件 安装扩展插 ...

  2. SpringBoot (三) :SpringBoot使用Freemarker模板引擎渲染web视图

    什么是Freemarker FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页.电子邮件.配置文件.源代码等)的通用工具. 它不是面向最终用户的,而 ...

  3. SpringBoot-web开发(三): 模板引擎Thymeleaf

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) 目录 1. 引入 ...

  4. 二分查找模板全面总结

    二分查找 二分法的引入 情形1 1.X的平方根 2.搜索旋转排序数组 情形2 1.第一个错误的版本 2.寻找峰值 3.寻找旋转排序数组中的最小值 情形3 在排序数组中查找第一个和最后一个位置 当遇到查 ...

  5. Django 模板HTML转义和CSRF4.3

    Django对字符串进行自动HTML转义,如在模板中输出如下值: 视图代码: def index(request):return render(request, 'temtest/index2.htm ...

  6. Django 模板继承4.2

    模板继承 模板继承可以减少页面内容的重复定义,实现页面内容的重用 典型应用:网站的头部.尾部是一样的,这些内容可以定义在父模板中,子模板不需要重复定义 block标签:在父模板中预留区域,在子模板中填 ...

  7. Django 模板4.1

    模板介绍 作为Web框架,Django提供了模板,可以很便利的动态生成HTML 模版系统致力于表达外观,而不是程序逻辑 模板的设计实现了业务逻辑(view)与显示内容(template)的分离,一个视 ...

  8. Django 视图和模板1.4

    视图 在django中,视图对WEB请求进行回应 视图接收reqeust对象作为第一个参数,包含了请求的信息 视图就是一个Python函数,被定义在views.py中 #coding:utf-8 fr ...

  9. [JAVA EE] Thymeleaf 高级用法:模板布局,带参数的引用片段,表单验证,常用校验注解

    模板布局 公共部分通常定义为模板布局:如页眉,页脚,公共导航栏.菜单等. 模板布局定义方法 布局页中用 th:fragment 定义模板片段,其他页面用 th:insert 引用片段 例如:foote ...

  10. [JAVAEE] 理解“自然模板“+使用 ModelAndView 对象

    接上一篇: https://blog.csdn.net/qq_36286039/article/details/119955773 不启动服务器,直接使用浏览器打开览页面 显示的是静态数据 Thyme ...

最新文章

  1. linux 替换内核 img,查看更改linux内核initrd.img-Go语言中文社区
  2. mysql建帐号数据库出现反斜线_[MySQL FAQ]系列 -- 账号密码包含反斜线时怎么办
  3. Dubbo错误排查:com.alibaba.dubbo.rpc.RpcException: Invoke remote method timeout
  4. 川崎机器人总线通信_【川崎】川崎机器人PROFINET 总线通信图文教程(上)
  5. 比特币 交易程序 php,比特币PHP离线交易开发包
  6. ORA-01925:maximum of 80 enabled roles exceeded
  7. python必读5本书籍_免费下载!5本从Python入手机器学习的必备电子书!(附链接)...
  8. 装B指南之使用浏览器播放电影
  9. 信息学奥赛一本通 1140:验证子串 | OpenJudge NOI 1.7 18
  10. Android Studio控制台输出乱码,编译时期输出乱码,outpot乱码解决
  11. 宏碁 Aspire E1-471g黑苹果efi引导文件
  12. DMX协议和RDM协议
  13. android手机变windows8,你真没有看错!Android手机一秒变Win10
  14. #NOIP模拟赛#捕鼠器mousetrap(树)
  15. 笔记本电脑怎么在桌面添加计算机,怎么在笔记本电脑上添加便签,笔记本电脑桌面便签设置密码...
  16. Windows组策略
  17. 计算机发展历史小报图片大全,【科技小报图片大全】科技小报简单又漂亮_科技小报内容资料大全_(2)_亲亲宝贝网...
  18. 运维派 » 你有自己的Web缓存知识体系吗?
  19. 微博机型Android怎么去掉,如何设置微博来源中显示出的手机型号 怎么去掉微博来源中的android字样...
  20. 多任务学习综述:推荐系统多任务学习(multitask)的实战总结和常见问题(一)

热门文章

  1. 我男朋友是项目经理......
  2. 学生信息管理系统简易版(文件读写操作)
  3. 关于matlab的hist函数取之后的折线
  4. jq双击放大图片_Jquery图片点击放大
  5. 互联网进入“降本增效”时代
  6. echarts 饼图label换行显示
  7. 越来越多中国企业为高通打call:移动计算走向更大舞台
  8. 3588 Rockchip_基于 DRM 框架的 HDMI 开发指南
  9. 我国茧丝绸行业概览及发展前景分析:蚕茧产量整体规模比较稳定[图]
  10. meminfo 解释