1. 简介

  • 模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
  • 模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
  • 模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
  • 每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector 或 vector 。

1.1 说明

  • 模板是一种对类型进行参数化的工具;
  • 通常有两种形式:函数模板和类模板;
  • 函数模板针对仅参数类型不同的函数;
  • 类模板针对仅数据成员和成员函数类型不同的类。

1.2 使用模版目的

能够让我们编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。

1.3 使用注意

模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

2. 函数模版

2.1 函数模板的格式:

template <typename type> ret-type func-name(parameter list)
{// 函数的主体
}

其中template和class是关见字,class可以用typename 关见字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为:

template <class T> void swap(T& a, T& b){}

当调用这样的模板函数时类型T就会被被调用时的类型所代替,比如swap(a,b)其中a和b是int 型,这时模板函数swap中的形参T就会被int 所代替,模板函数就变为swap(int &a, int &b)。而当swap(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。

#include <iostream>
#include <string>using namespace std;template <typename T>
inline T const& Max (T const& a, T const& b)
{ return a < b ? b:a;
}
int main ()
{int i = 39;int j = 20;cout << "Max(i, j): " << Max(i, j) << endl; double f1 = 13.5; double f2 = 20.7; cout << "Max(f1, f2): " << Max(f1, f2) << endl; string s1 = "Hello"; string s2 = "World"; cout << "Max(s1, s2): " << Max(s1, s2) << endl; return 0;
}

结果:

Max(i, j): 39
Max(f1, f2): 20.7
Max(s1, s2): World

2.2 注意

对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。

3. 类模版

3.1 类模板的格式为:

template<class  形参名,class 形参名,…>   class 类名
{ ... };

类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如

template<class T> class A{public: T a; T b; T hy(T c, T &d);};

在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。

#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>using namespace std;template <class T>
class Stack { private: vector<T> elems;     // 元素 public: void push(T const&);  // 入栈void pop();               // 出栈T top() const;            // 返回栈顶元素bool empty() const{       // 如果为空则返回真。return elems.empty(); }
}; template <class T>
void Stack<T>::push (T const& elem)
{ // 追加传入元素的副本elems.push_back(elem);
} template <class T>
void Stack<T>::pop ()
{ if (elems.empty()) { throw out_of_range("Stack<>::pop(): empty stack"); }// 删除最后一个元素elems.pop_back();
} template <class T>
T Stack<T>::top () const
{ if (elems.empty()) { throw out_of_range("Stack<>::top(): empty stack"); }// 返回最后一个元素的副本 return elems.back();
} int main()
{ try { Stack<int>         intStack;  // int 类型的栈 Stack<string> stringStack;    // string 类型的栈 // 操作 int 类型的栈 intStack.push(7); cout << intStack.top() <<endl; // 操作 string 类型的栈 stringStack.push("hello"); cout << stringStack.top() << std::endl; stringStack.pop(); stringStack.pop(); } catch (exception const& ex) { cerr << "Exception: " << ex.what() <<endl; return -1;}
}

结果:

7
hello
Exception: Stack<>::pop(): empty stack

3.2 类模板对象的创建

比如一个模板类A,则使用类模板创建对象的方法为A m;在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int, double> m;类型之间用逗号隔开。

3.3 对于类模板,模板形参的类型必须在类名后的尖括号中明确指定。

比如A<2> m;用这种方法把模板形参设置为int是错误的(编译错误:error C2079: ‘a’ uses undefined class ‘A’),类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定A m。

3.4 在类模板外部定义成员函数的方法为:

template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},
比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:

template<class T1,class T2> void A<T1,T2>::h(){}。
注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。

3.5 再次提醒注意

模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

4 模板的形参

有三种类型的模板形参:类型形参,非类型形参和模板形参。

4.1 类型形参

  • 类型形参由关见字class或typename后接说明符构成,如template void h(T a){};其中T就是一个类型形参,类型形参的名字由用户自已确定。模板形参表示的是一个未知的类型。模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。
  • 不能为同一个模板类型形参指定两种不同的类型,比如templatevoid h(T a, T b){},语句调用h(2, 3.2)将出错,因为该语句给同一模板形参T指定了两种类型,第一个实参2把模板形参T指定为int,而第二个实参3.2把模板形参指定为double,两种类型的形参不一致,会出错。(针对函数模板)
  • 当我们声明类对象为:A a,比如templateT g(T a, T b){},语句调用a.g(2, 3.2)在编译时不会出错,但会有警告,因为在声明类对象的时候已经将T转换为int类型,而第二个实参3.2把模板形参指定为double,在运行时,会对3.2进行强制类型转换为3。当我们声明类的对象为:A a,此时就不会有上述的警告,因为从int到double是自动类型转换。
#ifndef TEMPLATE_DEMO_HXX
#define TEMPLATE_DEMO_HXXtemplate<class T> class A{public:T g(T a,T b);A();
};#endifTemplateDemo.cpp#include<iostream.h>
#include "TemplateDemo.h"template<class T> A<T>::A(){}template<class T> T A<T>::g(T a,T b){return a+b;
}void main(){A<int> a;cout<<a.g(2,3.2)<<endl;
}

C++ 模版类和模板函数介绍及使用相关推荐

  1. C++ and Java template class and function 模板类和模板函数

    在C++和Java的泛式编程中,模板template的使用是必不可少的,但是Java中没有template关键字,所以两者的写法还是有些许区别的,请参见如下代码: Java的模板 // Java pu ...

  2. C++关于DLL导出模板类和模板函数

    这两天写了个Dll,要导出普通类中的模板函数,稍微查了一下,没查到具体资料.自己根据C++模板的编译原理,推断出应该要源码放在头文件中直接导出,查了下接触的Open Source项目,确实如此.这里记 ...

  3. C++之模板类模板函数

    模板类 模板类的定义使用template<typename T>或template<class T>,将具有相同功能的代码合并,增加代码的简洁性和易读性.例如在计算凸包的例子中 ...

  4. 【C++】模板类的友元函数

    模板类友元函数 模板类的友元函数 参考:https://blog.csdn.net/dreamer_lhs/article/details/53580088 区分:友元是否为函数模板 非模板友元 约束 ...

  5. C++ STL 标准模板库介绍与入门

    目录 1.概述 1.1.C++ 标准库 1.2.Boost库 2.STL 版本 2.1.HP 原始版本 2.2.P. J. 实现版本 2.3.RW 实现版本 2.4.SGI 实现版本 2.5.STLp ...

  6. C++中模板类使用友元模板函数 和友员非模版函数!使用不当出现编译错误:无法解析的外部符号…

    在c++海大本科课程设计的最后章节(模板使用 )中涉及到了在Mat类模板中定义友元函数friend Mat<T> operator+(Mat<T> &m, T num) ...

  7. 泛函编程—模板函数_类模板

    函数业务逻辑一样,只是函数参数类型不同 函数模板的本质:类型参数化--泛型编程 语法: template <typename T> template <class T1,class ...

  8. 8-1日复习 模板函数 模板类

    函数的重载: //函数重载 感觉还是太繁琐 引入函数模板的概念#include <iostream>using namespace std;int add(int x , int y) { ...

  9. 模板类可以使用虚函数,模板函数不能是虚函数

    1.普通的类中怎么使用虚函数,模板类中也可以这么使用虚函数 不过需要注意的是使用模板类定义不同的类型则是两个完全不同的类. 2.模板函数不能是虚函数 编译器期望在处理类定义的时候就能确定虚函数表的大小 ...

最新文章

  1. CVPR 2021评审出炉,评审员奇葩意见遭热议 | AI日报
  2. 使用gotoxy()函数移动控制台光标
  3. Android Java 自定义异常
  4. android 高级项目,从零开始的Android新项目8 - Data Binding高级篇
  5. BZOJ 1011: [HNOI2008]遥远的行星
  6. 列举网络芳邻的网络资源
  7. ajax数据交互代码,Django中使用jquery的ajax进行数据交互的实例代码
  8. RAD Studio 2010 环境设置(转)
  9. java servlet,action,struts,struts2输出流中文编码问题
  10. 8086、80286、80386
  11. 计算机函数sumif怎么用,怎么用sumif函数求和
  12. do vis是什么意思_duck不必什么梗?李佳琦放过鸭子吧表情包
  13. python将文件另存为,python读取文件另存为
  14. docker 命令补全
  15. 容联云实现手机验证码
  16. 数字艺术藏品系统开发
  17. 事务与共识 DDIA小结
  18. jmp指令(0903)
  19. MyBatis从入门到精通(九):MyBatis高级结果映射之一对一映射
  20. 【ICDAR 2023 X 阿里安全】挑战赛正式启动!篡改文本分类和检测两大赛题!

热门文章

  1. 生硬的论文,非得写什么架构设计
  2. 用Python画等边三角形
  3. 全球及中国丝蛋白行业研究及十四五规划分析报告
  4. 基于评论的推荐系统综述
  5. x在计算机中是哪个按键,电脑键盘x号怎么打出来
  6. MathType怎么编辑等号带点
  7. 2017找工作的经历,给求职小伙伴的一些建议
  8. 双重福利:计算机图书满100减50+满99 减10叠加券,更有抽奖送书活动,点击查看!...
  9. moment判断日期时间是否在另一个日期时间之前
  10. 清华刘知远:好的研究想法从哪里来?