C++函数模板与类模板的区别

C++函数模板与类模板的区别类模板 C 除了支持函数模板 还支持类模板 ClassTemplat 函数模板中定义的类型参数可以用在函数声明和函数定义中 类模板中定义的类型参数可以用在类声明和类实现中 类模板的目的同样是将数据的类型参数化 声明类模板的语法为 template lt typename 类型参数 1 typename 类型参数 2 gt class 类名 nbsp nbsp TODO

类模板:

  • x = 10、y = 10
  • x = 12.88、y = 129.65
  • x = “东京180度”、y = “北纬210度”
  
  1. template<typename T1, typename T2> //这里不能有分号
  2. class Point{
  3. public:
  4. Point(T1 x, T2 y): m_x(x), m_y(y){ }
  5. public:
  6. T1 getX() const; //获取x坐标
  7. void setX(T1 x); //设置x坐标
  8. T2 getY() const; //获取y坐标
  9. void setY(T2 y); //设置y坐标
  10. private:
  11. T1 m_x; //x坐标
  12. T2 m_y; //y坐标
  13. };

x 坐标和 y 坐标的数据类型不确定,借助类模板可以将数据类型参数化,这样就不必定义多个类了。

注意:模板头和类头是一个整体,可以换行,但是中间不能有分号。

上面的代码仅仅是类的声明,我们还需要在类外定义成员函数。在类外定义成员函数时仍然需要带上模板头,格式为:

template<typename 类型参数1 , typename 类型参数2 , …>
返回值类型 类名<类型参数1 , 类型参数2, ...>::函数名(形参列表){

    //TODO:
}


第一行是模板头,第二行是函数头,它们可以合并到一行,不过为了让代码格式更加清晰,一般是将它们分成两行。

下面就对 Point 类的成员函数进行定义:

  
  1. template<typename T1, typename T2> //模板头
  2. T1 Point<T1, T2>::getX() const /*函数头*/ {
  3. return m_x;
  4. }
  5.  
  6. template<typename T1, typename T2>
  7. void Point<T1, T2>::setX(T1 x){
  8. m_x = x;
  9. }
  10.  
  11. template<typename T1, typename T2>
  12. T2 Point<T1, T2>::getY() const{
  13. return m_y;
  14. }
  15.  
  16. template<typename T1, typename T2>
  17. void Point<T1, T2>::setY(T2 y){
  18. m_y = y;
  19. }

请读者仔细观察代码,除了 template 关键字后面要指明类型参数,类名 Point 后面也要带上类型参数,只是不加 typename 关键字了。另外需要注意的是,在类外定义成员函数时,template 后面的类型参数要和类声明时的一致。

使用类模板创建对象

上面的两段代码完成了类的定义,接下来就可以使用该类创建对象了。使用类模板创建对象时,需要指明具体的数据类型。请看下面的代码:

  
  1. Point<int, int> p1(10, 20);
  2. Point<int, float> p2(10, 15.5);
  3. Point<float, char*> p3(12.4, "东京180度");

与函数模板不同的是,类模板在实例化时必须显式地指明数据类型,编译器不能根据给定的数据推演出数据类型。

除了对象变量,我们也可以使用对象指针的方式来实例化:

  
  1. Point<float, float> *p1 = new Point<float, float>(10.6, 109.3);
  2. Point<char*, char*> *p = new Point<char*, char*>(“东京180度”, “北纬210度”);

需要注意的是,赋值号两边都要指明具体的数据类型,且要保持一致。下面的写法是错误的:

  
  1. //赋值号两边的数据类型不一致
  2. Point<float, float> *p = new Point<float, int>(10.6, 109);
  3. //赋值号右边没有指明数据类型
  4. Point<float, float> *p = new Point(10.6, 109);

综合示例

将上面的类定义和类实例化的代码整合起来,构成一个完整的示例,如下所示:

  
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. template<class T1, class T2> //这里不能有分号
  5. class Point{
  6. public:
  7. Point(T1 x, T2 y): m_x(x), m_y(y){ }
  8. public:
  9. T1 getX() const; //获取x坐标
  10. void setX(T1 x); //设置x坐标
  11. T2 getY() const; //获取y坐标
  12. void setY(T2 y); //设置y坐标
  13. private:
  14. T1 m_x; //x坐标
  15. T2 m_y; //y坐标
  16. };
  17.  
  18. template<class T1, class T2> //模板头
  19. T1 Point<T1, T2>::getX() const /*函数头*/ {
  20. return m_x;
  21. }
  22.  
  23. template<class T1, class T2>
  24. void Point<T1, T2>::setX(T1 x){
  25. m_x = x;
  26. }
  27.  
  28. template<class T1, class T2>
  29. T2 Point<T1, T2>::getY() const{
  30. return m_y;
  31. }
  32.  
  33. template<class T1, class T2>
  34. void Point<T1, T2>::setY(T2 y){
  35. m_y = y;
  36. }
  37.  
  38. int main(){
  39. Point<int, int> p1(10, 20);
  40. cout<<“x=”<<p1.getX()<<“, y=”<<p1.getY()<<endl;
  41.  
  42. Point<int, char*> p2(10, “东京180度”);
  43. cout<<“x=”<<p2.getX()<<“, y=”<<p2.getY()<<endl;
  44.  
  45. Point<char*, char*> *p3 = new Point<char*, char*>(“东京180度”, “北纬210度”);
  46. cout<<“x=”<<p3->getX()<<“, y=”<<p3->getY()<<endl;
  47.  
  48. return 0;
  49. }

函数模板:

在《C++函数重载》一节中,为了交换不同类型的变量的值,我们通过函数重载定义了四个名字相同、参数列表不同的函数,如下所示:

  
  1. //交换 int 变量的值
  2. void Swap(int *a, int *b){
  3. int temp = *a;
  4. *a = *b;
  5. *b = temp;
  6. }
  7.  
  8. //交换 float 变量的值
  9. void Swap(float *a, float *b){
  10. float temp = *a;
  11. *a = *b;
  12. *b = temp;
  13. }
  14.  
  15. //交换 char 变量的值
  16. void Swap(char *a, char *b){
  17. char temp = *a;
  18. *a = *b;
  19. *b = temp;
  20. }
  21.  
  22. //交换 bool 变量的值
  23. void Swap(bool *a, bool *b){
  24. char temp = *a;
  25. *a = *b;
  26. *b = temp;
  27. }

这些函数虽然在调用时方便了一些,但从本质上说还是定义了三个功能相同、函数体相同的函数,只是数据的类型不同而已,这看起来有点浪费代码,能不能把它们压缩成一个函数呢?

能!可以借助本节讲的函数模板。

我们知道,数据的值可以通过函数参数传递,在函数定义时数据的值是未知的,只有等到函数调用时接收了实参才能确定其值。这就是值的参数化。

在C++中,数据的类型也可以通过参数来传递,在函数定义时可以不指明具体的数据类型,当发生函数调用时,编译器可以根据传入的实参自动推断数据类型。这就是类型的参数化。

值(Value)和类型(Type)是数据的两个主要特征,它们在C++中都可以被参数化。

所谓函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板(Function Template)。

在函数模板中,数据的值和类型都被参数化了,发生函数调用时编译器会根据传入的实参来推演形参的值和类型。换个角度说,函数模板除了支持值的参数化,还支持类型的参数化。

一但定义了函数模板,就可以将类型参数用于函数定义和函数声明了。说得直白一点,原来使用 int、float、char 等内置类型的地方,都可以用类型参数来代替。

下面我们就来实践一下,将上面的四个Swap() 函数压缩为一个函数模板:















  
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. template<typename T> void Swap(T *a, T *b){
  5. T temp = *a;
  6. *a = *b;
  7. *b = temp;
  8. }
  9.  
  10. int main(){
  11. //交换 int 变量的值
  12. int n1 = 100, n2 = 200;
  13. Swap(&n1, &n2);
  14. cout<<n1<<“, “<<n2<<endl;
  15.  
  16. //交换 float 变量的值
  17. float f1 = 12.5, f2 = 56.93;
  18. Swap(&f1, &f2);
  19. cout<<f1<<“, “<<f2<<endl;
  20.  
  21. //交换 char 变量的值
  22. char c1 = ‘A’, c2 = ‘B’;
  23. Swap(&c1, &c2);
  24. cout<<c1<<“, “<<c2<<endl;
  25.  
  26. //交换 bool 变量的值
  27. bool b1 = false, b2 = true;
  28. Swap(&b1, &b2);
  29. cout<<b1<<“, “<<b2<<endl;
  30.  
  31. return 0;
  32. }
  
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. template<typename T> void Swap(T &a, T &b){
  5. T temp = a;
  6. a = b;
  7. b = temp;
  8. }
  9.  
  10. int main(){
  11. //交换 int 变量的值
  12. int n1 = 100, n2 = 200;
  13. Swap(n1, n2);
  14. cout<<n1<<", "<<n2<<endl;
  15.  
  16. //交换 float 变量的值
  17. float f1 = 12.5, f2 = 56.93;
  18. Swap(f1, f2);
  19. cout<<f1<<", "<<f2<<endl;
  20.  
  21. //交换 char 变量的值
  22. char c1 = 'A', c2 = 'B';
  23. Swap(c1, c2);
  24. cout<<c1<<", "<<c2<<endl;
  25.  
  26. //交换 bool 变量的值
  27. bool b1 = false, b2 = true;
  28. Swap(b1, b2);
  29. cout<<b1<<", "<<b2<<endl;
  30.  
  31. return 0;
  32. }

引用不但使得函数定义简洁明了,也使得调用函数方便了很多。整体来看,引用让编码更加漂亮。

下面我们来总结一下定义模板函数的语法:

template <typename 类型参数1 , typename 类型参数2 , ...> 返回值类型  函数名(形参列表){

    //在函数体中可以使用类型参数
}

类型参数可以有多个,它们之间以逗号,分隔。类型参数列表以< >包围,形式参数列表以( )包围。

typename关键字也可以使用class关键字替代,它们没有任何区别。C++ 早期对模板的支持并不严谨,没有引入新的关键字,而是用 class 来指明类型参数,但是 class 关键字本来已经用在类的定义中了,这样做显得不太友好,所以后来 C++ 又引入了一个新的关键字 typename,专门用来定义类型参数。不过至今仍然有很多代码在使用 class 关键字,包括 C++ 标准库、一些开源程序等。

本教程会交替使用 typename 和 class,旨在让读者在别的地方遇到它们时不会感觉陌生。更改上面的 Swap() 函数,使用 class 来指明类型参数:



  
  1. template<class T> void Swap(T &a, T &b){
  2. T temp = a;
  3. a = b;
  4. b = temp;
  5. }
  
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. //声明函数模板
  5. template<typename T> T max(T a, T b, T c);
  6.  
  7. int main( ){
  8. //求三个整数的最大值
  9. int i1, i2, i3, i_max;
  10. cin >> i1 >> i2 >> i3;
  11. i_max = max(i1,i2,i3);
  12. cout << “i_max=” << i_max << endl;
  13.  
  14. //求三个浮点数的最大值
  15. double d1, d2, d3, d_max;
  16. cin >> d1 >> d2 >> d3;
  17. d_max = max(d1,d2,d3);
  18. cout << “d_max=” << d_max << endl;
  19.  
  20. //求三个长整型数的最大值
  21. long g1, g2, g3, g_max;
  22. cin >> g1 >> g2 >> g3;
  23. g_max = max(g1,g2,g3);
  24. cout << “g_max=” << g_max << endl;
  25.  
  26. return 0;
  27. }
  28.  
  29. //定义函数模板
  30. template<typename T> //模板头,这里不能有分号
  31. T max(T a, T b, T c){ //函数头
  32. T max_num = a;
  33. if(b > max_num) max_num = b;
  34. if(c > max_num) max_num = c;
  35. return max_num;
  36. }

两者的区别:

函数模板与类模板有什么区别?答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化 必须由程序员在程序中显式地指定。

 

即函数模板允许隐式调用和显式调用而类模板只能显示调用这期间有涉及到函数模板与模板函数,类模板与模板类的概念(类似于类与类对象的区别)请看下面例子

注意:模板类的函数声明和实现必须都在头文件中完成,不能像普通类那样声明在.h文件中实现在.cpp文件中,原因可以看链接http://hi.baidu.com/cn_rigel/blog/item/6cf6fc083723e2286a60fb53.html

 

#include "stdafx.h"
#include <iostream>
using namespace std;

//使用模板创建一个返回最大值的函数
//这是一个函数模板
template <class Type>
Type MaxValue(Type a,Type b)
{

    if ( a > b)
    {

        return a;
    }
    else
        return b;
}

//创建一个堆栈模板类
//这是一个类模板
template <class T>
class Stack
{

public:
    Stack(){ m_nPos = 0;}
    ~Stack(){}

    void Push(T value);
    T Pop();

    bool IsEmpty()
    {

        return m_nPos == 0;
    }
    bool HasElement()
    {

        return !IsEmpty();
    }
    bool IsFull()
    {

        return m_nPos == STATCK_SIZE;
    }

private:
    int m_nPos;
    //使用常量表示堆栈的大小

    const static int STATCK_SIZE = 100;
    T m_Data[STATCK_SIZE];
};
//模板类的成员函数实现

template <class T>
void Stack<T> ::Push(T value)
{

    //使用后置递增操作符

    m_Data[m_nPos++] = value;
}
template <class T>
T Stack<T>::Pop()
{

    //使用前置递减操作符

    return m_Data[--m_nPos];
}

void TestMaxValue()
{

    //隐式调用
 




































































//函数模板的实例化在程序调用时自动完成
   cout << MaxValue(100, 204)<< endl;//MaxValue(100, 204)这是一个模板函数
    cout << MaxValue(2.5002,30.003) << endl;//MaxValue(2.5002,30.003)这也是一个模板函数
//当然由程序员自己指定也可以
    //显示调用

    cout << MaxValue<int>(10,20) << endl;
    cout << MaxValue<double>(2.5002,30.003) << endl;
}

void TestStack()
{

    //测试模板类(整数)

    Stack <int> intStack;//类模板的实例化由程序员显示的指定
    intStack.Push(10);
    intStack.Push(20);
    intStack.Push(30);

    while (intStack.HasElement())
    {

        cout << intStack.Pop() << endl;
    }

    //测试模板类(浮点)

    Stack <float> floatStack;//类模板的实例化由程序员显示的指定
    floatStack.Push(1.001);
    floatStack.Push(2.002);
    floatStack.Push(3.003);

    while (floatStack.HasElement())
    {

        cout << floatStack.Pop() << endl;
    }

    //测试动态创建对象

    //Stack创建的指针必须指明类型

    Stack<int>* pInt = new Stack<int>();类模板的实例化由程序员显示的指定
    pInt->Push(10);








































区别2:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/231895.html原文链接:https://javaforall.net

(0)
上一篇 2025年11月15日 上午9:01
下一篇 2025年11月15日 上午9:22


相关推荐

  • tp3 进行模糊查询

    tp3 进行模糊查询tp 模糊查询用数组 map nickname array LIKE nickname PHP 显示当前年月日 dump date Y m d exit

    2026年3月18日
    1
  • 子网掩码,反掩码与通配符之间的区别

    子网掩码,反掩码与通配符之间的区别1:子网掩码与反掩码的区别:反掩码就是通配符掩码通过标记0和1告诉设备应该匹配到哪位copy。由于跟子网掩码刚好相zd反,所以也叫反掩码例如掩码是255.255.255.0wildcard-mask就是0.0.0.255255.255.255.248反掩就是0.0.0.72:通配符掩码,ospf和Acl这儿用通配符掩码也不是每家的交换机都这么做,像cisco3550就是用的子网…

    2022年7月19日
    40
  • psd 替换智能图层的的实现-个性化定制网站

    psd 替换智能图层的的实现-个性化定制网站老板让做一个在线服装定制的网站,可合成服装的效果图遇到了难处,如果是单纯的图片叠加也比较简单,前端合成的话使用canvas两张图片合成在一起就可以了canvas合成衣服的效果图准备两张图片,一张是素材,一张是背景如下图他们加一块就得到了这样一张图满心欢喜找老板,实现了!!!,终于可以早早下班了,然并卵,老板说:素材为啥没有弯曲,做出来的图片不真实,方案被打回来之后再次研究方案1.弯曲写死,如果单纯定制杯子是没有问题的,因为他只有一种效果,如果定制的是衣服,风景画等等其他的商品效.

    2022年5月16日
    38
  • .gitignore不起作用解决

    .gitignore不起作用解决今天提交新项目时发现虽然已经有了 gitignore 文件 但是还是出现了 idea 以及 node modules 查阅了一下资料 gitignore 不起作用的原因是新建的文件在 git 中会存在缓存 如果有些文件已经被纳入了版本管理控制中 这时就算有 gitignore 文件也不会生效 需要清除缓存 步骤如下 gitrm rcached gitadd gitcommit m update gitignore 问题解决 参考资料 https blog csdn net zero

    2026年3月26日
    2
  • sql语句的执行顺序

    sql语句的执行顺序理解 sql 语句的执行顺序对优化 sql 非常重要 那么 sql 语句的执行顺序是怎样的呢 以一条简单的的语句做分析 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp 这一条语句包含我们经常用到的一些关键字 select from where groupby orderby 它的执行顺序如下 先执行 from 关键字后面的语句 明确数据的来源 它是从哪张表取来的 接着执行 where 关键字后面的语句 对数据进行筛选 再接

    2026年3月18日
    3
  • ag-grid 设置单元格以及行的颜色「建议收藏」

    ag-grid 设置单元格以及行的颜色「建议收藏」在使用ag-grid的时候有通过单元格的值设置不同行颜色,然后百度了网上的方法,汇总了一下,具体效果图如下:话不多说,直接上代码。<!doctypehtml><html><head><metacharset=”utf-8″><metaname=”viewport”content=”width=device-width,initial-scale=1,shrink…

    2022年7月11日
    20

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号