C++ 函数模板和类模板

news2024/9/24 10:19:53

参考视频:C++类模板_哔哩哔哩_bilibili

遗留问题:编译器怎么处理函数模板和类模板

目录

一、为什么会有函数模版?函数模板是为了解决什么问题?

二、函数模板的概念

三、函数模版的使用

四、函数模板的特化

五、类模板的概念

六、类模板的使用

七、类模板的特化

八、类模板的偏特化


一、为什么会有函数模版?函数模板是为了解决什么问题?

     函数模板是为了减少重复代码,是整合多个函数的函数体相同而区别只在参数类型不同的代码冗余的情况。不用函数模板前,多个函数的参数类型不同,但函数体相同,导致重复代码较多。有了函数模板,只需要一份函数体一份代码就可以实现原来需要几个函数体才实现的功能,例如不使用函数模板时,要分别对int、char等类型的两个数据做是否相等判断,需要定义多个函数:

bool isEqual( int nA,  int nB)
{
  return nA == nB;
}

bool isEqual(char cA, char cB)
{
  return cA == cB;
}

int main()
{
  int nA = 10;
  int nB = 20;
  char cA = 'A';
  char cB = 'B';
  std::cout << isEqual(nA, nB)<< std::endl;
  std::cout << isEqual(cA, cB) << std::endl;
  return 0;
}

 可见重载的两个函数的函数体实现一样,函数体代码重复,下面是使用函数模板的代码:

#include <iostream>
//使用模板代替原来的多个函数
template<typename T>
bool isEqual(T tA, T tB)
{
  return tA == tB;
}

int main()
{
//变量以匈牙利命名法命名
  int nA = 10;
  int nB = 20;
  char cA = 'A';
  char cB = 'B';
//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(int, int);
  std::cout << isEqual(nA, nB)<< std::endl;
//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(char, char);
  std::cout << isEqual(cA, cB) << std::endl;
  return 0;
}

二、函数模板的概念

       函数模板是用于生成函数的模板。在编译阶段,编译器会根据函数模板的使用情况(例如上面的isEqual(nA, nB)、isEqual(cA, cB)创建出函数名相同,参数类型由编译器判断的若干函数。通过函数模板创建的函数拥有相同的函数体,只是函数的参数类型不同。

三、函数模版的使用

        每当在一个编译单元(经过预处理的.cpp文件)中使用了函数模版,则必须在该编译单元中给出函数模版的定义(要是把函数模板声明写在.h文件,函数模板声明写在.cpp文件,然后在使用函数模板时只使用include 包含函数模板的头文件,不给出函数模板的定义,那么编译会报无法解析的外部符号错误)。因此,为了避免在每个编译单元都定义函数模板,建议在头文件中对函数模板进行声明定义(即将函数模板的声明和定义都写在一个头文件中,然后在需要使用函数模板的地方加上该头文件)。

   函数模版的声明:

template<typename T>

返回类型 函数名(参数列表);

其中T表示任意类型,参数的类型和返回类型都可以指定为T

函数模版的定义

template<typename T,..>  //可指定多个泛型
返回类型 函数名(参数列表)
{
  //函数体
}

将一、为什么会有函数模版?函数模板是为了解决什么问题?这一节中的函数模板的声明和定义写在isEqualFunc.h头文件中再使用:

#ifndef ISEQUALFUNC_H
#define ISEQUALFUNC_H
//函数模板声明
template<typename T>
bool isEqual(T tA, T tB);

//函数模板定义
template<typename T>
bool isEqual(T tA, T tB)
{
  return tA == tB;
}

#endif // ISEQUALFUNC_H
//这是main.cpp
#include "isEqualFunc.h"
#include <iostream>

int main()
{

    //变量以匈牙利命名法命名
      int nA = 100111111;
      int nB = 111111;
      char cA = 'A';
      char cB = 'A';
    //编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(int, int);
      std::cout << isEqual(nA, nB)<< std::endl;
    //编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(char, char);
      std::cout << isEqual(cA, cB) << std::endl;
      return 0;
}

四、函数模板的特化

函数模板特化是指在实例化函数模板时,对特定类型的实参进行特殊的处理,即当实参为特定类型时,通过函数模板生成的函数会有不同的函数体。

特化时需要为函数模板添加新的定义,方式如下:

template<>
返回类型 函数名<特定的参数类型>(参数列表)
{
  //函数体
}

在上面的例子中,对于char*类型的字符串,使用上面的函数模板无法对字符串正确判断是否相等,因为字符串比较不是直接比较指针是否相等,而要调用strcmp函数判断,对上面的例子就需要对函数模板进行特化

#ifndef ISEQUALFUNC_H
#define ISEQUALFUNC_H
#include <string.h>
//函数模板声明
template<typename T>
bool isEqual(T tA, T tB);


//函数模板定义
template<typename T>
bool isEqual(T tA, T tB)
{
  return tA == tB;
}

//函数模板的特化
template<>
bool isEqual< char* >( char* szA,  char* szB)
{
    return strcmp(szA, szB) == 0;
}

#endif // ISEQUALFUNC_H
//这是main.cpp
#include "isEqualFunc.h"
#include <iostream>

int main()
{

    //变量以匈牙利命名法命名
      int nA = 100111111;
      int nB = 111111;
      char cA = 'A';
      char cB = 'A';
      char szA[4] = "AAA";
      char szB[4] = "AAA";
     // 编译器会怎么处理函数模板和特化?待参考https://blog.csdn.net/zgaoq/article/details/85232968

    //编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(int, int);
      std::cout << isEqual(nA, nB)<< std::endl;
    //编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(char, char);
      std::cout << isEqual(cA, cB) << std::endl;

      std::cout << isEqual(szA, szB) << std::endl; //判断字符串是否相等

      return 0;
}

五、类模板的概念

类模板是用于生成类的模板。在编译阶段,编译器会根据类模板的使用情况创建出仅部分成员数据类型和部分成员函数的参数类型不同,其他完全相同的若干类。

通过类模板的这些特性我们可以尝试写出用于存放不同类型数据的容器。

六、类模板的使用

类模板的声明格式:

template<typename T,...>
class 类名
{
    //成员
};

类模板的成员函数定义如下,一般类模板的定义也应该写在头文件中: 

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

下面是一个例子,这个例子定义了一个MyArray的类模板,其中数据成员是数组,通过泛型T达到可定义各种类型数组的目的,而所有对这个数组的操作函数都相同,这样就不用针对不同类型的数组,定义不同的类,达到了复用代码即减少重复代码的目的:

//这是myArray.h
#ifndef MYARRAY_H
#define MYARRAY_H
#include <iostream>
constexpr int m_nMaxLen = 20;

template <typename T>
class MyArray
{
public:
    MyArray():size(0){}
    //添加数据
    bool addData(T value);
    //删除数据
    bool deleteData(T value);
    //获取数据
    T getData(int nIndex);
    void print()
    {
        for(int i = 0; i < size; i++)
        {
           std::cout << data[i] << std::endl;
        }
    }
private:
    T data[m_nMaxLen];
    int size;
};


template<typename T>
bool MyArray<T>::addData(T value)
{
    if(size == m_nMaxLen)
    {
        return false;
    }
    data[size] = value;
    size++;
    return true;
}



template<typename T>
bool MyArray<T>::deleteData(T value)
{
    if(size == 0)
    {
        return false;
    }
    int nDataIndex = -1;
   for(int i = 0; i < size; i++)
   {
    if(data[i] == value)
    {
        nDataIndex = i;
        break;
    }
   }
   if(nDataIndex == -1)
   {
     std::cout<< "找不到该数据!"  << std::endl;
     return false;
   }
   else
   {
     for(int i = nDataIndex; i < size -1; i++)
     {
         data[i] = data[i+1];  //往前移动后面的数据,即可以删除该数据
     }
     size --;
   }
    return true;
}


template<typename T>
T  MyArray<T>::getData(int nIndex)
{
    if(nIndex >= 0 && nIndex < size)
    {
        return data[nIndex];
    }
    else
    {
        return (T)0;
    }
}


#endif // MYARRAY_H
//这是main.cpp
#include "myarray.h"
#include <iostream>
int main()
{
      std::cout <<"----int begin----" << std::endl;
      MyArray<int> intData;
      for(int i = 1; i <= 10; i++)
      {
          intData.addData(i);
      }
      intData.print();
      std::cout <<"--------" << std::endl;
      intData.deleteData(11);
      std::cout <<"--------" << std::endl;
      intData.print();
      std::cout << intData.getData(0) << std::endl;
      std::cout <<"----int end----" << std::endl;


      std::cout <<"----char begin----" << std::endl;
      MyArray<char> charData;
      for(int i = 1; i <= 10; i++)
      {
          charData.addData('A'+ i);
      }
      charData.print();
      std::cout <<"--------" << std::endl;
      charData.deleteData('B');
      std::cout <<"--------" << std::endl;
      charData.print();
      std::cout << charData.getData(1) << std::endl;
      std::cout <<"----char end----" << std::endl;

      return 0;
}

七、类模板的特化

类模板的特化是在实例化类模板时,对特定类型的泛型进行特殊的处理,即用户指定所有特定类型的类模板时,通过特化类模板生成的类可能与其他类有完全不同的结构。

特化类模板是需要对整个类模板进行声明定义:

template<>
class 类名<指定类型,指定类型,...>
{
//类成员
};

这里对第六节中的例子添加特化:

#ifndef MYARRAY_H
#define MYARRAY_H

//....其他代码不变


//对特定泛型的类的成员函数进行特化
template<>
MyArray<char>::MyArray()
{
    std::cout<< "MyArray<char>::MyArray()" << std::endl;
}

//对特定泛型的类进行特化
//这里例子可能不太恰当,只是展示如何对特定类型的类进行特化
template<>
class MyArray<float>
{
  public:
    MyArray();
  private:
    float data;
};

MyArray<float>::MyArray()
{
    std::cout<< "MyArray<float>::MyArray()" << std::endl;
}

#endif // MYARRAY_H

八、类模板的偏特化

偏特化和特化类似,只是特化会指定所有的泛型,而偏特化只指定部分泛型。

偏特化类模板需要对整个类模板进行声明定义:

template <typename T,...不需特化的泛型...>
class 类名<指定类型,...不需特化的泛型名,...>
{
 //类成员;
}

例子:

#ifndef PAIR_H
#define PAIR_H
#include <iostream>
template<typename T1, typename T2>
class Pair
{
public:
    Pair();
private:
    T1 m_first;
    T2 m_second;
};

template<typename T1, typename T2>
Pair<T1, T2>::Pair()
{
    std::cout << "Pair<T1, T2>::Pair()" << std::endl;
}

//偏特化
template<typename T2>
class Pair<char, T2>
{
public:
    Pair();
private:
    char first;
    T2 second;
};

template<typename T2>
Pair<char, T2>::Pair()
{
    std::cout << "Pair<char, T2>::Pair()" << std::endl;
}

#endif // PAIR_H
//这是main.cpp
#include <iostream>
#include "Pair.h"
int main()
{
   Pair<int, int> pNn;
   Pair<char, int> pCn;
   return 0;
}

运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

24/8/7 算法笔记 支持向量机回归问题天猫双十一

import numpy as np from sklearn.svm import SVR import matplotlib.pyplot as plt X np.linspace(0,2*np.pi,50).reshape(-1,1) y np.sin(X) plt.scatter(X,y) 建模 线性核函数 svr SVR(kernel linear) svr.fit(X,y.ravel())#变成一维y_ svr.predict(X) plt.scatter(…

heapq.heapify构建小顶堆的流程

代码示例 import heapqlst [2, 3, 4, 6, 9, 1, 5] heapq.heapify(lst) print(lst)流程解释 初始列表: 列表 lst 在开始时是 [2, 3, 4, 6, 9, 1, 5]。 调用 heapq.heapify(lst): heapify 函数将 lst 转换为一个小顶堆&#xff08;min-heap&#xff09;。在小顶堆中&#xff0…

Spring框架漏洞(附修复方法)

Spring是Java EE编程领域的一个轻量级开源框架&#xff0c;该框架由一个叫Rod Johnson的程序员在2002年最早提出并随后创建&#xff0c;是为了解决企业级编程开发中的复杂性&#xff0c;业务逻辑层和其他各层的松耦合问题&#xff0c;因此它将面向接口的编程思想贯穿整个系统应…

《UniverSeg: Universal Medical Image Segmentation》ICCV2023

摘要 这篇论文提出了一种名为 UniverSeg 的方法&#xff0c;它能够解决未见过的医学图像分割任务&#xff0c;而无需额外的训练。现有的深度学习模型通常无法泛化到新的解剖结构、图像模式或标签上。UniverSeg 利用一种新的 CrossBlock 机制&#xff0c;通过查询图像和定义新分…

利用tkinter制作简易计算器,页面美观,计算保留4位小数

import tkinter as tk import time window tk.Tk() window.title("简易计算器") window.geometry(300x400) content def btn_onclick(data):global contentif data"AC" or data "MC":expression.set()result.set()content elif data :result…

无人机长生不老秘籍

机身保养 外观检查 1.检查机器表面整洁无划痕无针孔凹陷擦伤、畸变等损坏情况 2.晃动机身&#xff0c;仔细听机身内部有无松动零件或者螺丝在机身内部 桨叶检查 1.有无裂痕、磨损、变形等缺陷&#xff0c;如有明显缺陷建议更换 2.卡扣、紧固件有无松脱或失效&#xff0c;…

嵌入式学习之路 14(C语言基础学习——指针操作一维整型数组)

一、指针基础 指针的概念 地址表示内存单元的编号&#xff0c;也被称为指针。指针既是地址&#xff0c;也是一种专门用于处理地址数据的数据类型。 例如&#xff0c;变量a的地址或者十六进制表示的0x1000都可以视作指针。 指针变量的定义 语法&#xff1a;基类型 * 指针变…

探索灵办AI:智能办公的好帮手

引言 随着AI工具的增多&#xff0c;选择合适的AI助手变得尤为重要。ChatGPT的订阅费用高且功能单一&#xff0c;很多小伙伴开始寻找更具性价比和多功能的替代品。灵办AI以其便捷、高效、多功能的特点&#xff0c;成为许多朋友的新宠。 灵办AI助手是一款多功能的全能AI助手&am…

智能名片信息交流系统应用场景解决方案

智能名片作为传统名片在数字化时代的进化产物&#xff0c;集成了移动互联网、大数据和人工智能等先进技术&#xff0c;为商务交流和客户关系管理带来了全新的体验。 系统主要功能 01内容聚合与展示 企业信息展示&#xff1a;智能名片不仅包含个人基本信息&#xff0c;还能展示…

MDIO C22协议访问MMD寄存器

以太网PHY芯片中SMI(Serial Management Interface)串行管理接口,也称MDIO(Management Data Input/Output),通常作为MII管理接口(MII Management Interface)。有两根线,分别为双向的MDIO和单向的MDC,用于以太网设备中上层对物理层(PHY)的管理。 MDIO接口有两种协议…

LVX+keepalived群集

Keepalived 双 机 热 备 基 础 知 识 Keepalived 起初是专门针对LVS 设计的一款强大的辅助工具&#xff0c;主要用来提供故障切换(Failover) 和健康检查(Health Checking) 功能——判断 LVS负载调度器、节点服务器的可用性&#xff0c;当master 主机出现故障及时切换到b…

MacOS Anaconda 安装教程及虚拟环境创建

一、下载 Anaconda 1、Anaconda 官网 2、清华大学开源软件镜像站 点 Date 按时间排序&#xff0c;根据自己 Mac 芯片类型下载对应最新版本的。 Intel 芯片的下载 x86_64 版本的Apple m1 芯片的下载 arm64 版本的 二、安装 Anaconda 将安装包下载到本地后&#xff0c;双击安…

人工智能|人工智能教育的发展现状及趋势

智能的热潮正席卷全球。国家在人工智能领域展开战略布局&#xff0c;人工智能人才成为国家急需的高层次技术人才。据领英发布的《全球 Al 领域人才报告》显示&#xff0c;国内人工智能人才缺口达到 500 多万。 毫无疑问&#xff0c;人工智能将不可阻挡地影响所有产业。给自己一…

随手记1.0

easyexcel多级表头导出各级设置样式&#xff08;继承HorizontalCellStyleStrategy实现&#xff09; package com.example.wxmessage.entity;import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;…

探索 Amazon Q Developer 那些有趣的功能

我在 McKinsey 2024 年 5 月 30 日提供的一项名为“The state of AI in early 2024-Gen AI adoption spikes and starts to generate value”的调研中读到这么一句话&#xff1a;人工智能在组织中最常见的两个使用职能是&#xff1a;“市场营销和销售”以及“产品和服务开发”&…

vue3-ts:husky + prettier / 代码格式化工具

一、Prettier简介 Prettier是一个流行的代码格式化工具&#xff0c;它的主要作用是帮助开发者自动规范化代码的格式&#xff0c;提高代码的可读性和一致性。Prettier通过解析代码并使用自己的规则重新打印它&#xff0c;以确保代码风格的一致性和符合预设的格式化标准。 二、…

计网学习(四)——网络层

一、网际协议IP 互联网采用的设计思路&#xff1a;网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数据报服务”&#xff0c;即IP数据报在网络中传输时是不可靠的服务&#xff0c;可能会出现数据丢失等情况TCP/IP体系中网络层常常被称为网际层或IP层与IP协议一起使用…

35分钟教你从0到1,水出SCI(精品),学术裁缝必修课_来自B站水论文的程序猿

系列文章目录 文章目录 系列文章目录一、抽象理解二、具体做法1、什么是Baseline(基础实验&#xff09;2、怎么和导师说3、怎么做压低baseline 4、怎么写 一、抽象理解 一篇论文A:西红柿炒鸡蛋 一篇论文B:芹菜炒肉 你就可以:西红柿炒肉 二、具体做法 1、什么是Baseline(基础…

视频压缩文件太大了怎么缩小?6个视频压缩技巧,速度收藏起来!

高清视频文件&#xff0c;尤其是那些以 1080p 和 720p 清晰度为特征的视频&#xff0c;通常都拥有相当大的体积&#xff0c;会占据大量计算机存储空间。因此&#xff0c;为了更好地将它们进行分享和存储&#xff0c;您可能需要对它们进行压缩&#xff0c;以减小它们的尺寸。然而…

【秋招笔试】2024-08-07-YT游戏(研发岗)-三语言题解(CPP/Python/Java)

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 本次的题目比较典,…