模 板

news2024/11/24 20:07:18

导引:

模板是为了解决函数类型不同所重载,带来的麻烦简化。利用一个模板(示列)代码,让编译器编写出不同类型的代码,满足所需。

int swap(int &p1,int &p2)
{
    int p=p1;
    p1=p2;
    p2=p;
}
char swap(char &p1,char &p2)
{
    char p=p1;
    p1=p2;
    p2=p;
}

模板可以分为:函数模板与类模板。

写法与普通函数或类无异。

1.函数模板

1.1模板格式

template(后面可以续集)

返回值类型 函数名(参数)

{

    //........

}

其中函数返回值与参数多以模板定义T1为类型(根据需求自行斟酌)。

仿函数与函数模板的区别

  1. 语法和结构:仿函数是一个类,通过重载"operator()"来实现函数行为;而函数模板则是一个模板化的函数定义。
  2. 类型参数化:仿函数通常不直接支持类型参数化,但可以通过模板类来实现;而函数模板则直接支持类型参数化,可以处理任意类型的参数。
  3. 使用场景:仿函数更适用于需要保存状态或行为的上下文,比如算法中的回调函数或比较函数;而函数模板则更适用于那些行为不依赖于状态,但需要处理多种数据类型的场景。
  4. 性能:由于仿函数是对象,因此在使用时可能会涉及到对象的创建、销毁和传递等开销;而函数模板在实例化后就是普通的函数,通常没有这些额外的开销。

函数模板的特化

函数模板的特化是对模板函数的一种定制或特化形式,用于处理模板函数不能很好处理的特定类型或情况。当模板函数对于某些类型不能提供合适的实现时,我们可以为这些类型提供特化版本。特化允许我们为特定的类型或类型组合提供定制的函数实现。

特化分为全特化(Full Specialization)和部分特化(Partial Specialization)。全特化是为特定的类型参数列表提供完整的替代实现,而部分特化则允许我们为模板参数的一部分提供特化实现

区别:

  1. 目的:仿函数的目的是提供可像函数一样调用的对象,通常用于算法和需要自定义行为的场景。函数模板的特化的目的是为模板函数提供针对特定类型的定制实现,以优化或扩展模板函数的行为。
  2. 语法和结构:仿函数是类,具有类的定义和成员函数的语法。函数模板的特化则遵循模板特化的语法,需要使用template<>关键字,并指定特化的类型。
  3. 类型参数化:仿函数通常不直接支持类型参数化,但它们可以嵌套在模板类中,从而间接地支持类型参数化。函数模板的特化则直接处理类型参数化,为特定的类型或类型组合提供定制的实现。
  4. 使用场景:仿函数在需要自定义比较、转换或算法行为时非常有用。函数模板的特化则在模板函数需要针对特定类型进行特殊处理时使用,例如当默认实现不高效或不适用于某些类型时。
  5. 性能和效率:仿函数的性能可能受到对象创建和销毁的影响,但在需要保存状态或行为的情况下,这是可以接受的。函数模板的特化通常与模板函数的性能相当,因为它们都是编译时确定的。

1.2使用模型

示例:

#include<iostream>
using namespace std;
template<typename T>;
T summation(const T ret1,const T ret2)
{
    return ret1+ret2;
}  
int main()
{
 int ret=summation(10,20);
 return 0;   
}

注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

1.3原理

编译器编写代码的原理与形式:

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。

1.4函数模板实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化显式实例化

隐式实例化与显示实例化:

#include<iostream>
using namespace std;
template<typename T>;
T summation(const T ret1,const T ret2)
{
    return ret1+ret2;
}  
int main()
{
 int ret=summation(10,20);
 double ret1=summation(10.0,20.0);
 //但是如果两个类型不同就会出现错误。
 //如:
 //int ret2=summation(10,20.0);
 //编译器就会出现报错,《不能确定您到底要定义为int还是double类型》
 解决方法:
 //1.强转
 int ret2=summation(10,(int)20.0);
 // 2.显示实例化
 //函数名<类型>(参数);
  int ret2=summation<int>(10,20.0);
 //  3.添加模板定义
 //在template<typename T>中,再添加一个"  typename T1  ".
 //T summation(const T ret1,const T1 ret2)
 //该方法与函数无异,实属无奈使用。
 return 0;   
}

1.5匹配规则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
// 专门处理int的加法函数
int Add(int left, int right)
{
 return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
 return left + right;
}
void Test()
{
 Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
 Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

2.类模板

2.1 类模板的定义格式

template<class T1, class T2, ..., class Tn>

class 类模板名

      // 类内成员定义

};

注意:类模板中函数放在类外进行定义时,需要加模板参数列表

template <class T>

Vector<T>::~Vector()

{

   //......

}

2.2 类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>然后将实例化的类型放在<>中即可,且与模板对象个数相同,类模板名字不是真正的类,而实例化的结果才是真正的类。

// Vector类名,Vector<int>才是类型

Vector<int> s1;

Vector<double> s2;

2.3 类模板与函数模板的区别

类模板与函数模板区别主要有两点:

  • 类模板没有自动类型推导的使用方式
  • 类模板在模板参数列表中可以有默认参数

 

//类模板与函数模板的区别
template<class NameType, class AgeType = int> //指定默认参数
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }

    void showPerson()
    {
        cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};


void test01()
{
    //Person p("孙悟空", 1000);错误的,类模板无法用自动类型推导
    Person<string, int>p("孙悟空", 1000);//正确,只能用显式指定类型推导
    p.showPerson();
}

void test02()
{
    Person<string>p("猪八戒", 999); //类模板在参数列表中有默认参数
}
   
int main()
{
    test01();
    system("pause");
    return 0;
}

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

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建

2.5类模板对象做函数参数

学习目标:类模板实例化出的对象,向函数传参的方式。

一共有三种传入方式:

  • 指定传入的类型:直接显示对象的数据类型
  • 参数模板化:将对象中的参数变为模板进行传递
  • 整个类模板化:将这个对象类型模板化进行传递

如: 

//类模板对象做函数参数
template<class T1,class T2>
class Person
{
public:
    Person(T1 name,T2 age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }

    void showPerson()
    {
        cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
    }

    T1 m_Name;
    T2 m_Age;
};

//1、指定传入类型
void printPerson1(Person<string, int>&p)
{
    p.showPerson();
}
void test01()
{
    Person<string, int>p("孙悟空", 199);
    printPerson1(p);
}

   
// 2、参数模板化
template<class T1,class 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>p("猪八戒", 90);
    printPerson2(p);
}

// 3、整个类模板化
template<class T>
void printPerson3(T &p)
{
    p.showPerson();
    cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03()
{
    Person<string, int>p("唐僧", 60);
    printPerson3(p);
}

int main()
{
    test01();
    test02();
    test03();
    system("pause");
    return 0;
}

注:使用比较广泛的是指定传入类型的传参方式。

2.6 类模板与继承

当类模板碰到继承时,需要注意以下几点:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需为类模板
//类模板与继承
template<class T>
class Base
{
    T m;
};

//class Son: public Base //错误,必须要知道父类中的T类型,才能继承给子类
class Son :public Base<int>
{

};

void test01()
{
    Son s1;
}

//如果想灵活指定父类中T的类型,子类也需要变成类模板
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 obj;
};

void test02()
{
    Son2<int,char> s2;
}

int main()
{
    test02();
    
    system("pause");
    return 0;
}

2.7类模板成员函数类外实现

代码理解:

//类模板成员类外实现
template<class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age);
    /*{
        this->m_Name = name;
        this->m_Age = age;
    }*/

    void showPerson();
    /*{
        cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
    }*/

    T1 m_Name;
    T2 m_Age;
};

//构造函数的类外实现
template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}


//成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}

void test01()
{
    Person<string, int>p("Tom", 30);
    p.showPerson();
}


int main()
{
    test01();
    
    system("pause");
    return 0;
}

2.8类模板分文件编写

如果工程中需要利用多个类模板,那么将这些类模板都写在同一个文件中将会导致代码可读性变差,所以有必要对类模板进行分文件编写,但是类模板的分文件编写面临着一些问题,以下是类模板分文件编写面临的问题及解决方法。

问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。

解决:

解决方式1:直接包含.cpp源文件(粗暴)

解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制的

示例1:(未进行分文件编写)

template<class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age);
    /*{
        this->m_Name = name;
        this->m_Age = age;
    }*/

    void showPerson();
    /*{
        cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
    }*/

    T1 m_Name;
    T2 m_Age;
};

//构造函数的类外实现
template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}


//成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}

void test01()
{
    Person<string, int>p("Tom", 30);
    p.showPerson();
}


int main()
{
    test01();
    
    system("pause");
    return 0;
}

例2:(进行分文件编写,利用.cpp)

1.创建头文件person.h,写一些声明

#pragma once
#include <iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age);
    void showPerson();

    T1 m_Name;
    T2 m_Age;
};

2.创建person.cpp,写具体实现

 #include "person.h"

//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}


//成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}

实例3:(分文件编写,利用.hpp)

将person.h和person.cpp的内容写到一起,并将后缀名改为.hpp,这是类模板分文件编写最常用的方式

1.编写person.hpp文件:

#pragma once
#include <iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age);
    void showPerson();

    T1 m_Name;
    T2 m_Age;
};

//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}


//成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}

2.编写main函数

#include <iostream>
using namespace std;
#include <string>
#include "person.hpp"

void test01()
{
    Person<string, int>p("Tom", 30);
    p.showPerson();
}


int main()
{
    test01();
    
    system("pause");
    return 0;
}

 

2.9类模板与友元

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

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

1.全局函数的类内实现

template<class T1, class T2>
class Person
{
    //全局函数类内实现
    friend void printPerson(Person<T1, T2> p)
    {
        cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
    }
public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    
private:
    T1 m_Name;
    T2 m_Age;
};

void test01()
{
    Person<string, int>p("Tom", 30);
    printPerson(p);
}
int main()
{
    test01();
    
    system("pause");
    return 0;
}

2.全局函数类外实现

//提前让编译器知道Person类的存在
template<class T1, class T2>
class Person;

//类外实现
template<class T1, class T2>
void printPerson(Person<T1, T2> p)
{
    cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
    //全局函数类外实现 
    //加空模板参数列表
    //如果全局函数是类外实现,需要让编译器提前知道这个函数的存在
    friend void printPerson<>(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;
};



void test01()
{
    Person<string, int>p("Tom", 30);
    printPerson(p);
}


int main()
{
    test01();
    
    system("pause");
    return 0;
}

注:需要注意各个函数声明之间的顺序。在Person类模板中有友元的声明friend void printPerson<>(Person p),因为类模板中友元的类外实现需要让编译器提前知道这个函数,所以需要将printPerson函数写在前面。而printPerson函数中又涉及Person类,所以在printPerson函数前面需要提前声明Person类模板的存在

总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别。

 3.模板的优缺点

优点:

1.模板复用了代码,使其节约资源,更快的迭代开发,

2.增加了代码灵活性

缺点:

1.模板会导致代码膨胀,导致时间变长(动态库可调集【未改变】)

2.信息凌乱,不易查找。

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

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

相关文章

优化安防视频监控的关键体验:视频质量诊断技术如何应用在监控系统中?

随着科技的不断进步&#xff0c;视频监控平台在公安、司法、教育、基础设施等众多领域得到了广泛应用。然而&#xff0c;视频图像的质量直接关系到监控系统的应用效果&#xff0c;是反映监控系统运维效果的重要指标之一。因此&#xff0c;视频监控平台需要配备一系列先进的视频…

基于剂型改良的复杂注射剂分析!

改良型新药在医药领域的重要性日益凸显&#xff0c;其中脂质体注射剂作为一类重要的改良型新药&#xff0c;因其独特的临床优势和技术创新&#xff0c;正受到行业的高度关注。本文基于药融咨询团队的深度分析报告&#xff0c;探讨脂质体注射剂的技术创新、市场前景以及在中国的…

动手学深度学习(三)深度学习计算

一、模型构造 1、继承Module类来构造模型来构造模型 class MLP(nn.Module):# 声明带有模型参数的层&#xff0c;这里声明了两个全连接层def __init__(self, **kwargs):# 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数# 参数&#xff0c…

利用CubeMX复现正点原子TFTLCD驱动例程

来源&#xff1a;正点原子 FMC的工作原理暂时先欠着&#xff0c;先记录一下CRUD的过程。 第一步准备一个us级别延时函数&#xff0c;不会的参考拙作&#xff1a;STM32的定时器简介-CSDN博客 第二部开启FMC外设&#xff1a; ①进入 Pinout->FMC 配置栏&#xff0c;配置 …

【隐私计算】Paillier半同态加密算法

一、何为同态加密&#xff08;HE&#xff09;&#xff1f; HE是一种特殊的加密方法&#xff0c;它允许直接对加密数据执行计算&#xff0c;如加法和乘法&#xff0c;而计算过程不会泄露原文的任何信息。计算的结果仍然是加密的&#xff0c;拥有密钥的用户对处理过的密文数据进…

树莓派5开发板-安装Raspberry Pi系统-学习记录1

树莓派5开发板介绍 树莓派5&#xff08;Raspberry Pi 5&#xff09;是树莓派系列最新的开发板&#xff0c;相较于前几代产品&#xff0c;它在性能、连接性和功能方面都有了显著提升。以下是树莓派5的一些主要特点&#xff1a; 处理器&#xff1a;树莓派5搭载了Broadcom BCM27…

如何基于gpt模型抢先打造成功的产品

来自&#xff1a;Python大数据分析 费弗里 ChatGPT、gpt3.5以及gpt4&#xff0c;已然成为当下现代社会中几乎人尽皆知的话题&#xff0c;而当此种现象级产品引爆全网&#xff0c;极大程度上吸引大众注意力的同时&#xff0c;有一些嗅觉灵敏的人及时抓住了机会&#xff0c;通过快…

【FreeRL】我的深度学习库构建思想

文章目录 前言参考python环境效果已复现结果 综述DQN.py&#xff08;主要&#xff09;算法实现参数修改细节实现显示训练&#xff0c;保存训练 Buffer.pyevaluate.pylearning_curves 前言 代码实现在:https://github.com/wild-firefox/FreeRL 欢迎star 参考 动手学强化学习e…

Coggle数据科学 | 小白学 RAG:Milvus 介绍与使用教程

本文来源公众号“Coggle数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;小白学 RAG&#xff1a;Milvus 介绍与使用教程 什么是Milvus&#xff1f; Milvus 是一款高性能、高扩展性的开源向量数据库&#xff0c;专为处理…

【阿一网络安全】如何让你的密码更安全?(三) - 散列函数

散列函数 散列函数&#xff08;Hash Function&#xff0c;又称散列算法、哈希函数&#xff09;&#xff0c;是一种从任何一种数据中创建小的数字指纹的方法。 散列值 散列函数&#xff0c;把任意长的消息明文&#xff0c;压缩成摘要&#xff0c;使得数据量变小&#xff0c;将…

[数据集][目标检测]脊椎检测数据集VOC+YOLO格式1137张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1137 标注数量(xml文件个数)&#xff1a;1137 标注数量(txt文件个数)&#xff1a;1137 标注…

SpringBoot2:web开发常用功能实现及原理解析-上传与下载

文章目录 一、上传文件1、前端上传文件给Java接口2、Java接口上传文件给Java接口 二、下载文件1、前端从Java接口下载文件2、Java接口调用Java接口下载文件 一、上传文件 1、前端上传文件给Java接口 Controller接口 此接口支持上传单个文件和多个文件&#xff0c;并保存在本地…

基于小程序的教学辅助微信小程序设计+ssm(lw+演示+源码+运行)

教学辅助微信小程序 摘 要 随着移动应用技术的发展&#xff0c;越来越多的学生借助于移动手机、电脑完成生活中的事务&#xff0c;许多的传统行业也更加重视与互联网的结合&#xff0c;由于学生学习的压力越来越大&#xff0c;教学辅助是一个非常不错的教育平台&#xff0c;对…

人工智能(AI)领域各方向顶会和顶刊

在人工智能&#xff08;AI&#xff09;这个快速发展的领域&#xff0c;研究人员和从业者需要紧跟最新的研究动态和技术进展。顶级的会议和期刊是获取最新科研成果和交流思想的重要平台。以下是人工智能领域内不同方向的顶级会议和期刊概览。 顶级会议 人工智能基础与综合 A…

客厅无主灯设计:灯位布局与灯光灯具的和谐搭配

在现代家居设计中&#xff0c;客厅作为家庭活动的中心区域&#xff0c;其照明设计的重要性不言而喻。无主灯设计以其灵活多变、氛围营造独特的优势&#xff0c;逐渐成为客厅照明的热门选择。然而&#xff0c;如何合理规划灯位布局&#xff0c;并科学搭配灯光与灯具&#xff0c;…

20240913 每日AI必读资讯

AMD死战CUDA&#xff1a;我是一家软件公司 - AMD重大改变&#xff1a;重心将从硬件开发转向强调软件开发、API 和 AI 体验。 - 软件工程团队规模扩大了三倍&#xff0c;并且全力以赴投入软件开发 - AMD将自家已有5年历史的图形架构RDNA、计算架构CDNA重新整合在一起&#xf…

计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

2024年9月13日 十二生肖 今日运势

小运播报&#xff1a;2024年9月13日&#xff0c;星期五&#xff0c;农历八月十一 &#xff08;甲辰年癸酉月庚辰日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;猴、鼠、鸡 需要注意&#xff1a;牛、兔、狗 喜神方位&#xff1a;西北方 财神方位&#xff1a;…

windows10通过coursier安装scala

第一步&#xff1a;安装Java 参考菜鸟教程安装Java&#xff1a; https://www.runoob.com/java/java-environment-setup.html#win-install 第二步&#xff1a;安装coursier 进入https://www.scala-lang.org/download/ 如下图所示&#xff1a; 第三步、确定jdk对应的scala版本…

小琳AI课堂:MASS模型——革新自然语言处理的预训练技术

大家好&#xff0c;这里是小琳AI课堂。今天我们来聊聊一个在自然语言处理&#xff08;NLP&#xff09;领域非常热门的话题——MASS模型&#xff0c;全称是Masked Sequence to Sequence Pre-training for Language Generation。这是华为诺亚方舟实验室在2019年提出的一种创新模型…