C++中的Template

news2024/11/18 11:17:12

模板的概念

建立通用的模具,大大提高复用性

模板不可直接使用

函数模板

函数模板语法

函数模板作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

语法:

template<typename T>;
//函数声明或定义

解释

template -- 声明创建模板

typename -- 表明其后面的符号是一种数据类型,可以用class代替

T -- 通用的数据类型,名称可替换,通常为大写字母

//利用模板提供通用的交换函数
template<typename T>
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}
void test01()
{
    int a = 10;
    int b = 20;
    //利用模板实现交换
    //1. 自动类型推导
    mySwap(a, b);
    cout << "a = " << a << ", b = " << b << endl;
    //2. 显示指定类型
    mySwap<int>(a, b);
    cout << "a = " << a << ", b = " << b << endl;
    double c = 2.2;
    double d = 2.1;
    mySwap(c, d);
    cout << "c = " << c << ", d = " << d << endl;
}

注意事项

自动类型推导,必须推导出一致的数据类型T,才可以使用

模板必须要确定出T的数据类型,才可以使用

template<typename T>
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}
test02()
{
    int a = 1;
    char b = 'c';
    mySwap(a, b);//推导不出一致的数据类型
}
template<typename T>
void func()
{
}
test02()
{
    func<int>(); //必须指定T的数据类型,不能只写func();
}

函数模板案例

案例描述:

//利用模板提供通用的交换函数
template<typename T>
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}
//打印数组模板
template<typename T>
void myPrint(T arr[], int len)
{
    for (int i = 0; i < len; ++i)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}
template<typename T>
void sort(T arr[], int len)
{
    for (int i = 0; i < len; ++i)
    {
        int max = i;
        for (int j = i + 1; j < len; ++j)
        {
            if (arr[max] < arr[j])
            {
                mySwap(arr[max], arr[j]);
            }
        }
        if (max != i)
        {
            mySwap(arr[max], arr[i]);
        }
    }
}
void test01()
{
    //测试char数组
    char charArr[] = "badcfe";
    int len = sizeof(charArr) / sizeof(char);
    sort(charArr, len);
    myPrint(charArr, len);

    //测试int数组
    int intArr[] = { 0, 5, 6, 4, 2, 8,5,9,3 };
    len = sizeof(intArr) / sizeof(int);
    sort(intArr, len);
    myPrint(intArr, len);
}

普通函数与函数模板的区别

//普通函数
int myAdd01(int a, int b)
{
    return a + b;
}
//函数模板
template<typename T>
T myAdd02(T a, T b)
{
    return a + b;
}
void test01()
{
    int a = 10;
    char b = 'b'; //a -97, b - 98
    cout << myAdd01(a, b) << endl;
    //自动类型推导,不会发生隐式类型转换
    //cout << myAdd02(a, b) << endl; //报错
    //显示指定类型,会发生隐式类型转换
    cout << myAdd02<int>(a, b) << endl; 
}

建议使用显示指定类型方式

普通函数与函数模板的调用规则

调用规则:

//普通函数
void myPrint(int a, int b)
{
    cout << "调用的普通函数" << endl;
}
//函数模板
template<typename T>
void myPrint(T a, T b)
{
    cout << "调用的函数模板" << endl;
}
template<typename T>
void myPrint(T a, T b, T c)
{
    cout << "调用重载的函数模板" << endl;
}
void test01()
{
    int a = 10;
    int b = 20;
    myPrint(a, b); //调用普通函数
    //空模板参数列表
    myPrint<>(a, b);
    myPrint(a, b, 12);
    //更好的匹配
    char c = 'a';
    char d = 'a';
    myPrint(c, d); //调用函数模板
}

模板的局限性

class Person
{
public:
    Person(string name, int age)
    {
        m_Name = name;
        m_Age = age;
    }
    //方法1. 重载加号
    /*bool operator==(Person p)
    {
        if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
        {
            return true;
        }
        return false;
    }*/
    string m_Name;
    int m_Age;
};
template<typename T>
bool myCompare(T a, T b)
{
    if (a == b)
    {
        return true;
    }
    else
    {
        return false;
    }
}
//方法2. 为特定类型提供具体的化的模板
template<> bool myCompare(Person a, Person b)
{
    if (a.m_Name == b.m_Name && a.m_Age == b.m_Age)
    {
        return true;
    }
    else
    {
        return false;
    }
}
void test01()
{
    Person p1("Tom", 10);
    Person p2("Tom", 10);
    bool ret = myCompare(p1, p2);
    if (ret)
    {
        cout << "p1==p2" << endl;
    }
    else
    {
        cout << "p1!=p2" << endl;
    }
}

类模板

类模板语法

作用:建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表

语法:

template<typename T>
类

解释

template -- 声明创建模板

typename -- 表明其后面的符号是一种数据类型,可以用class代替

T -- 通用的数据类型,名称可替换,通常为大写字母

template<typename nameType, typename ageType>
class Person
{
public:
    Person(nameType name, ageType age)
    {
        m_Name = name;
        m_Age = age;
    }
    void showPerson()
    {
        cout << "名称:" << this->m_Name << ", 年龄:" << this->m_Age << endl;
    }
    nameType m_Name;
    ageType m_Age;
};


void test01()
{
    Person<string, int> p1("Tom", 10);
    Person<string, int> p2("Joy", 20);
    p1.showPerson();
    p2.showPerson();

}

类模板与函数模板区别

//类模板与函数模板区别  int 默认参数
template<typename nameType, typename ageType = int>
class Person
{
public:
    Person(nameType name, ageType age)
    {
        m_Name = name;
        m_Age = age;
    }
    void showPerson()
    {
        cout << "名称:" << this->m_Name << ", 年龄:" << this->m_Age << endl;
    }
    nameType m_Name;
    ageType m_Age;
};
void test01()
{
    //1. 类模板没有自动类型推导使用方式,函数模板有
    //Person p("Tom", 10); //错误,无法用自动类型推导
    Person<string, int> p1("Tom", 10); //正确,只能用显示指定类型
    p1.showPerson();
    //2. 类模板在模板参数列表中可以有默认参数,函数模板不可以有
    Person<string>p2("Joy", 20);
    p2.showPerson();
}

类模板中成员函数创建时机

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建
class Person1
{
public:
    void showPerson1()
    {
        cout << "Person1 show" << endl;
    }
};
class Person2
{
public:
    void showPerson2()
    {
        cout << "Person2 show" << endl;
    }
};
template<typename T>
class MyClass
{
public:
    T obj;
    //类模板中的成员函数,并不是一开始就创建
    void func1()
    {
        obj.showPerson1();
    }
    void func2()
    {
        obj.showPerson2();
    }
};
void test01()
{
    MyClass<Person1> p;
    p.func1();
    //p.func2(); //编译会出错,说明函数调用才会去创建成员函数
}

类模板对象做函数参数

template<typename T1, typename T2>
class Person
{
public:
    Person(T1 name, T2 age)
    {
        m_Name = name;
        m_Age = age;
    }
    void showPerson()
    {
        cout << "姓名:" << this->m_Name << ",年龄:" << this->m_Age << endl;
    }
    T1 m_Name;
    T2 m_Age;
};
//方法1. 直接显示对象的数据类型
void printPerson(Person<string, int> p)
{
    p.showPerson();
}
void test01()
{
    Person<string, int> p1("孙悟空", 100);
    printPerson(p1);
}
//方法2. 参数模板化
template<typename T1, typename T2>
void printPerson2(Person<T1, T2>& p)
{
    p.showPerson();
    cout << "T1的数据类型为:" << typeid(T1).name() << endl;
    cout << "T2的数据类型为:" << typeid(T2).name() << endl;
}
void test02()
{
    Person<string, int> p1("沙和尚", 40);
    printPerson2(p1);
}
//方法3. 整个类模板化
template<typename T>
void printPerson3(T & p)
{
    p.showPerson();
    cout << "T的数据类型为:" << typeid(T).name() << endl;
}
void test03()
{
    Person<string, int> p1("唐僧", 30);
    printPerson3(p1);
}

类模板与继承

template<class T>
class Base
{
public:
    T m;
};
class Son : public Base<int>
{
    
};

template<class T1, class T2>
class Son2 : public Base<T2>
{
public:
    Son2()
    {
        cout << "T1的类型为:" << typeid(T1).name() << endl;
        cout << "T2的类型为:" << typeid(T2).name() << endl;
    }
    T1 s;
};

void test01()
{
    Son p1;
    Son2<int, char> S2;
}

类模板成员函数类外实现

template<typename T1, typename T2>
class Person
{
public:
    Person(T1 name, T2 age);
    void showPerson();
    T1 m_Name;
    T2 m_Age;
};
//构造函数 类外实现
template<typename T1, typename T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    m_Name = name;
    m_Age = age;
}
//成员函数 类外实现
template<typename T1, typename T2>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << ",年龄:" << this->m_Age << endl;
}
void test01()
{
    Person<string, int> P("tom", 20);
    P.showPerson();
}

类模板份文件编写

//第一种解决方式,直接包含 源文件
//#include "person.cpp"
//第二种解决方式,将.h和.cpp内容写到一起,将后缀名改为.hpp文件,类模板
#include "person.hpp"
void test01()
{
    Person<string, int> P("tom", 20);
    P.showPerson();
}

person.hpp

#pragma once
#include <iostream>
using namespace std;
template<typename T1, typename T2>
class Person
{
public:
    Person(T1 name, T2 age);
    void showPerson();
    T1 m_Name;
    T2 m_Age;
};
//成员函数类外实现
template<typename T1, typename T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    m_Name = name;
    m_Age = age;
}
template<typename T1, typename T2>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << ",年龄:" << this->m_Age << endl;
}

总结:主流的解决方式第二种,将类模板成员函数写到一起,将后缀名改为.hpp

类模板与友元

全局函数类内实现:直接在类内声明友元即可

全局函数类外实现:需要提前让编译器知道全局函数的存在

//2. 全局函数配合友元 类外实现
//先做函数模板声明
template<class T1, class T2>
class Person;
//如果声明了函数模板,可以将实现写到后面,
//否则需要将实现体写到类的前面让编译器提前看到
template<class T1, class T2>
void printPerson2(Person<T1, T2> &p);
    
template<class T1, class T2>
class Person
{
    //1. 全局函数配合友元 类内实现
    friend void printPerson(Person<T1, T2> &p)
    {
        cout << "姓名:" << p.m_Name << ",年龄:" << p.m_Age << endl;
    }
    //全局函数配合友元 类外实现
    //加空模板参数列表
    //如果全局函数是类外实现,需要让编译器提前知道这个函数的存在
    friend void printPerson2<>(Person<T1, T2> &p);
public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
private:
    T1 m_Name;
    T2 m_Age;
};
template<typename T1, typename T2>
void printPerson2(Person<T1, T2>& p)
{
    cout << "姓名:" << p.m_Name << ",年龄:" << p.m_Age << endl;
}

void test01()
{
    Person<string, int> P("tom", 20);
    printPerson(P);
    Person<string, int> P2("joy", 34);
    printPerson2(P2);
}

类模板案例

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

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

相关文章

小程序社区团购demo

概述 实现了用户登录或者手机号&#xff0c;加入团长&#xff0c;邀请团长&#xff0c;各种佣金明细等页面 详细 需求&#xff1a; 根据市场信息反馈&#xff0c;社区团购比较火&#xff0c;有流量的用户可以推广页面 实现了功能&#xff1a; 实现了用户微信登录自动获取…

vue之 h() 函数

前言 Vue推荐在绝大数情况下使用模板来创建HTML&#xff0c;然后一些特殊的场景&#xff0c;你真的需要JavaScript的完全编程的能力&#xff0c;这个时候你可以使用渲染函数 &#xff0c;它比模板更接近编译器&#xff1b; h()函数是什么 Vue在生成真实的DOM之前&#xff0c…

如何用好免费的ChatGPT

如何用好免费的ChatGPT 前言ChatGPT使用入口在线体验地址&#xff1a;点我体验 ChatGPT介绍ChatGPT初级使用技巧初级使用技巧&#xff1a;清晰明了的问题表达 ChatGPT中级使用语法中级使用语法&#xff1a;具体化问题并提供背景信息 ChatGPT高级使用高级使用&#xff1a;追问、…

Vue中如何进行表格排序与过滤

Vue中如何进行表格排序与过滤 在Vue.js中&#xff0c;表格是一个常见的数据展示方式。很多时候&#xff0c;我们需要对表格中的数据进行排序和过滤&#xff0c;以提供更好的用户体验。本文将介绍如何在Vue中实现表格的排序和过滤功能&#xff0c;并提供相关的代码示例。 准备工…

WebSocket通信安全概览

文章前言 在一次做项目的时候本来是想去点击Burpsuite的Proxy界面的HTTP History选项卡来查看HTTP历史请求记录信息并做测试的&#xff0c;但是在查看的时候却下意识的点击到了HTTP Proxy右侧的"WebSockets History"选项卡中&#xff0c;从界面的交互历史中发现网站…

2023华为杯数模C题——大规模创新类竞赛评审方案研究

B题——大规模创新类竞赛评审方案研究 思路&#xff1a;采用数据分析等手段改进评分算法性能 完成情况(1-2问已经完成) 代码下载 问题一 在每个评审阶段&#xff0c;作品通常都是随机分发的&#xff0c;每份作品需要多位评委独立评审。为了增加不同评审专家所给成绩之间的可比…

c语言代码

目录 1.利用goto的关机程序 2.交换两个整数&#xff08;容易出现的错误&#xff09; 2.1 错误示范 2.1.1 错误的原因 3.函数调用进行折半查找 3.1错误版本 3.1.1错误原因 4.写一个函数&#xff0c;每调用一次这个函数&#xff0c;就会将num的值增加1。 4.1使用传值进去 5.有关p…

【初阶数据结构】二叉树全面知识总结

二叉树详解 树的概念及其结构树的概念树的相关概念树的表示方法孩纸兄弟表示法双亲表示法&#xff08;并查集&#xff09; 树的实际应用 二叉树二叉树的概念二叉树的种类二叉树的性质二叉树的存储结构 二叉树顺序结构的实现堆的概念及结构堆向上、向下调整法堆的插入堆的删除堆…

基于ENC28J60+uIP1.0+STM32的UDP Server实现,服务器主动发送数据的实现,几个关键的问题可算整明白了!

ENC28J60&#xff0c;是一款SPI接口的以太网PHYMAC芯片&#xff0c;实现以太网物理层和MAC层硬件通信。uIP是一个TCP/IP软件协议栈&#xff0c;实现TCP、UDP、ARP、ICMP等网络协议。STM32F103RCT6通过SPI接口与ENC28J60通讯&#xff0c;并移植uIP协议&#xff0c;实现一个小型的…

20230224_HDR-ISP_环境配置

https://github.com/JokerEyeAdas/HDR-ISP/tree/main 一、环境配置 *.VS2015编译不过&#xff1b;VS2017没问题 *.将红框部分复制到工程下面 *.添加头文件路径 *.添加原文件

【C#】Redis在net core下使用教程

系列文章 文章目录 系列文章前言一、Redis 简介1.1 Redis 优势1.2 Redis与其他key-value存储有什么不同&#xff1f; 二、Redis安装步骤2.1 下载链接2.2 安装测试 三、Redis修改帐户密码四、Redis写成Windows服务五、.net core - 使用CSRedisCore操作redis 前言 官方教程&…

【C语言】模拟实现内存函数

本篇文章目录 相关文章1. 模拟 memcpy 内存拷贝2. 模拟 memmove 内存移动 相关文章 【C语言】数据在内存中是以什么顺序存储的&#xff1f;【C语言】整数在内存中如何存储&#xff1f;又是如何进行计算使用的&#xff1f;【C语言】利用void*进行泛型编程【C语言】4.指针类型部…

相机有俯仰角时如何将像素坐标正确转换到其他坐标系

一般像素坐标系转相机坐标系都是默认相机是水平的&#xff0c;没有考虑相机有俯仰角的情况&#xff0c;大致的过程是&#xff1a;像素坐标系统-->图像坐标系-->相机坐标系 ->世界坐标系或雷达坐标系: 像素坐标系 像素坐标系&#xff08;u&#xff0c;v&#xff09;是…

【Java 基础篇】Java函数式接口详解

Java是一门强类型、面向对象的编程语言&#xff0c;但在Java 8引入了函数式编程的概念&#xff0c;这为我们提供了更多灵活的编程方式。函数式接口是函数式编程的核心概念之一&#xff0c;本文将详细介绍Java函数式接口的概念、用法以及一些实际应用。 什么是函数式接口&#…

JUC第八讲:Condition源码分析

JUC第八讲&#xff1a;Condition源码分析 本文是JUC第八讲&#xff0c;Condition详解。任意一个Java对象&#xff0c;都拥有一组监视器方法&#xff08;定义在java.lang.Object上&#xff09;&#xff0c;主要包括 wait()、wait(long timeout)、notify()以及notifyAll()方法&am…

【LeetCode-中等题】513. 找树左下角的值

文章目录 题目方法一&#xff1a;前序递归方法二&#xff1a;层序遍历 题目 方法一&#xff1a;前序递归 在递归遍历到叶子结点时&#xff0c;对比此时的节点深度&#xff0c;若当前节点深度大于当前最大深度&#xff0c;就更新value值&#xff0c;最后记录下的value即为最下最…

elementui 菜单选中优化

/** 父级菜单悬浮样式**/ .el-submenu__title:hover {color:#1890ff!important; } /** 父级菜单箭头悬浮样式**/ .el-submenu__title:hover>.el-submenu__icon-arrow{font-size: 13px!important;} /** 子菜单悬浮样式**/ .el-menu-item:hover{color:#1890ff!important; } /*…

Linux操作系统基础详解,计算机专业必看!

目录 Linux操作系统 Linux 简介 Linux 接口 Linux 组成部分 Shell Linux 应用程序 Linux 内核结构 Linux 进程和线程 基本概念 Linux 进程间通信 Linux 中进程管理系统调用 Linux 进程和线程的实现 Linux 调度 Linux 启动 Linux 内存管理 基本概念 Linux 内存…

【owt】 Intel® Media SDK for Windows: MSDK2021R1

https://www.intel.com/content/www/us/en/developer/articles/tool/media-sdk.html官方网不提供下载了: 2021地址 直接下载: MSDK2021R1.exe老版本 Intel Media SDK(Windows版本) 大神的介绍:owt-client-native 需要 https://github.com/open-webrtc-toolkit/owt-client…

spring security auth2.0实现

OAuth 2.0 的认证/授权流程 jwt只是认证中的一步 4中角色 资源拥有者&#xff08;resource owner&#xff09;、客户端&#xff08;client 第三方&#xff09;、授权服务器&#xff08;authorization server&#xff09;和资源服务器&#xff08;resource server&#xff09;。…