首页 > 编程语言 >C++模板

C++模板

时间:2023-09-01 20:33:21浏览次数:34  
标签:函数 C++ template 类型 模板 特化

一、类型信息运算符

typeid

在C++中typeid可以获取数据的类型,但是需要加头文件typeinfo

find /usr/include -name typeinfo
  1. typeid是运算符,执行运算符函数,执行的返回类型是type_info类类型对象

  2. type_info中有个 name 的成员函数

  3. type_info中还重载了 == 运算符,可以直接比较两个数据类型是否相同

  4. 它可以区分父类指针或引用指向的是哪个子类,但是父子类之间必须构成多态

二、模板

  1. 什么是模板
    是一种自动生成代码的技术,这种技术能让程序员在编写代码时不需要考虑数据类型,因此也称为泛型编程技术

  2. 为什么要使用模板
    ①C/C++/Java/C# 属于静态编程语言(编写->预处理->编译->汇编->链接->可执行文件)-强类型语言,静态编程语言优点是运行速度快,缺点代码改动后重新编译、实现代码通用比较麻烦

    Javascript/Python/shell 弱类型语言 动态编程语言

    ②C语言中的通用类型是通过 void* + 回调函数 实现的,实现难度大,使用比较麻烦
    ③借助定义宏的方式实现代码通用,但宏有一定的缺点:类型不检查、没有返回值、二义性等问题
    ④借助函数重载可以实现函数代码的通用,可能会导致代码段的增多,无法解决未知类型
    ⑤综上所诉,C++之父为了解决代码通用问题,实现了模板技术,让C++拜托数据困扰的问题

三 、函数模板

  1. 函数模板的定义

    template<typename T>
    void func(T num)
    {
        //具体功能
    }
    

    未知类型名可以任意取名字,一般约定为T

  2. 函数模板的原理
    函数模板会经历两次编译:
    ①先检查函数模板的语法是否有错误,如果无误,暂时也不会生成二进制指令,代码段中没有存储该函数模板
    ②根据调用者提供的实参类型再次编译检查函数模板代码,如果也没有错误,才会生成一份二进制指令存储在代码段中,所以如果函数模板没有任何一次调用,则不会生成任何二进制指令,如果有不同类型的实参调用函数模板,则会生成另一份二进制指令存储在代码段
    ③这种函数模板实例化称为“惰性实例化”准则

四、函数模板的调用

  • C++编译器不会把函数模板当作一个函数实体,而是当作生成函数实体的工具,当调用函数模板并提供了实际类型参数后,才会生成函数实体

  • 调用函数模板类型必须提供相应数量的类型参数

    • 自动:编译器会自动根据实参的类型,获取函数模板的类型

    • 手动:

      函数名<类型1,类型2,...>(实参)
      会根据<>中提供的类型名去生成函数实体
      
    • 默认形参类型

      template<typename T1,typename T2=int,typename T3=long>
      T3 函数名(T1 arg1,T2 arg2)
      {
          T3 ret = arg1 + arg2;
          return ret;
      }
      

      函数模板的类型参数可以像普通函数的默认形参设置形参值一样去设置默认的形参类型,靠右原则一致,但是该语法只有C++11后才支持 -std=gnu++0x

五、函数模板的特化

模板虽好,但不能直接解决所有类型的问题,有些特殊类型与普通类型的运算规则不同,例如char*,因此需要给这些特殊类型实现一个特殊版本,这种称为函数模板的特化

特化的方式:

  1. 通过typeid()分支判断类型执行特殊步骤

    if(typeid(T) == typeid(char*))
    
  2. 实现一个特化版本
    特化格式:

    template<>
    特类型返回值 函数名(特化类型 形参名){}
    

    注意:

    1. 特化前,必须有一个基础版本的函数模板

    2. template<>一定要加,才说明是函数模板的特化

    3. 特化函数的形参基础类型要与函数模板的形参基础类型相同,否则报错,例如函数模板中有引用,特化中也需要有

    4. 编译器会优先调用函数模板中的特化版本,因此不会与基础的函数模板有冲突

    5. 可以同时存在类型完全相同的普通函数、函数模板特化、函数模板,按照普通->特化->函数模板的顺序调用

    6. 普通函数无论是否被调用都会生成二进制指令,但是模板特化依然遵循"惰性实例化"准则,不被调用时不生成二进制指令

作业:实现二分查找、冒泡、插入、希尔、快排、归并、堆算法的函数模板

六、类模板

使用未知类型来设计类类型

  1. 类模板定义

    template<typename T1,typename T2>
    class 类名
    {
        T1 成员变量;
    public:
        T2 func(void)
        {
            T2 ret;
            return ret;
        }
    }
    
  2. 类模板的使用

    类必须先实例化才能使用,与函数模板不同的是不支持自动实例化,只能显式提供类型参数手动实例化

    类名<类型参数> 对象名;

    类名<类型参数>* 对象指针 = new 类名<类型参数>(构造函数的实参);

练习 :实现链式队列的类模板

  1. 类模板中的静态成员

    类模板中允许有静态成员,与普通类的静态成员变量一样需要在类外定义

    template<typename T>
    class 类名
    {
        static 类型名 num;
    };
    template<typename T> 类型名 
    Test<T>::num = 初始数据;
    //对于每个类模板实例化出来的类对象,也是共享静态成员变量
    
  2. 递归实例化

    什么类型都可以是模板的类型参数,包括类模板类型

    template<typename T>
    class A
    {
        T a;
    };
    
    template<typename T>
    class B
    {
        T b;
    };
    B<A<int>> b;    //  C++11之后才允许
    B<A<int> > b;   //  C++11之前必须加空格
    
  3. 类模板的默认参数类型

    与函数模板的默认参数类型规则一样

    template<typename T=类型名>
    class A
    {
        T a;
    };
    
  4. 类模板的局部特化

    当类模板中的成员函数不能支持所有类型时,可以针对不同类型实现类模板中的成员函数的特化版本,称为类模板的局部特化

    • 方法一:通过typeid比较类型,通过分支语句执行特殊操作

    • 方法二:通过实现局部特化成员函数来处理

      template<>
      int List<const char*>::find(const char* const& data)
      
    • 注意:

      • 一般在类外实现局部特化

      • 类型名的格式除了替换的typename的类型发生替换,其余所有格式都需要一直,例如常属性、引用等

  5. 类模板的全局特化

    为特殊类型重新特化一个类模板,称为类模板的全局特化

  6. 定义类模板和函数模板,class关键字可以替换typename

标签:函数,C++,template,类型,模板,特化
From: https://www.cnblogs.com/wangqiuji/p/17672788.html

相关文章

  • 标准C++ -- day09
    一、智能指针常规指针的缺点:当一个常规指针离开了作用域时,只有该指针变量本身占用的内存空间(4/8字节)会被释放,而它指向的内存空间不会自动释放,当free、delete、delete[]语句忘记执行或者无法执行,形成内存泄漏如何定位内存泄漏、如何预防内存泄漏智能指针的优点:......
  • 探索C++非质变算法:如何更高效地处理数据
    前言......
  • c++IO流——开工啦
    ......
  • jiangly算法模板收集
    目录数据结构树状数组并查集线段树其一其二其三懒标记线段树取模类(新版)取模类(旧版)树链剖分Splay其他二叉树其一其二分数四则运算数论欧拉筛组合数多项式相关几何图论(有向图)强连通分量缩点(无向图)求解割边、割边缩点一般图最大匹配(带花树算法)【久远】最大流费用流2-Sat【久远】字符......
  • Linux组件安装部署手册模板
    Linux系统-部署-运维系列导航 背景说明组件安装步骤是基本通用的,大部分组件安装都需要经过一些必须的流程,才能成为有效的服务。 本文以Linux(CentOS7)系统为基础介绍,其他操作系统原理一样,只是部分操作的具体执行方式需要根据操作系统调整。  根据经验总结,组件安装一般都......
  • c++并发编程实战-第2章 线程管控-读书笔记
    线程的基本管控每个应用程序都至少拥有一个线程,即运行main函数的线程,称为主线程,它由c++运行时系统启动。我们可以在软件运行中产生其他线程,它们以指定的函数作为入口函数。当main函数返回后,程序会退出;同样,当入口函数返回后,与之对应的线程结束。发起线程线程是通过构造std::thre......
  • 用C++如何进行判断
       ......
  • 如何进行C++动态转换
       ......
  • 16、C++ primer 变量声明和定义的关系P41
    1、什么是分离式编译?分离式编译(SeparateCompilation)是一种软件开发技术,用于将一个大型的程序分割成多个独立的源代码文件,并分别进行编译。每个源代码文件通常包含一个模块、类、函数或者其他逻辑单元的实现。2、声明使得名字为程序所知,一个文件如果想使用别处定义的名字则必须......
  • BOOL CALLBACK PromptProc(){} 这种符合c++ 的函数定义 格式吗?
       在C++中,函数的定义确实是由返回类型、函数名和参数列表组成的。但是在某些特殊的情况下,例如回调函数的定义,我们可能会看到类似于BOOLCALLBACKPromptProc(HWNDhwndDlg,UINTmessage,WPARAMwParam,LPARAMlParam)这样的函数定义。BOOLCALLBACK在这种情况下被用......