• C++模板介绍


    📋 个人简介

    • 💖 作者简介:大家好,我是菀枯😜

    • 🎉 支持我:点赞👍+收藏⭐️+留言📝

    • 💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️

      v2-af3cfbda99d44f8eb114cc6c74c92253_720w

      前言

    破防周(考试周)终于结束了,鄙人有幸存活了下来。上一次我们学习了C++中内存空间的分类,变量在内存中的存储位置以及C++中新引入的内存管理方式new和delete,今天我们来学习一下C++中泛型编程的典范:模板

    模板介绍

    在此之前我们学习了C++中新引入的新语法:函数重载。函数重载可根据传入的参数类型来判断该使用哪一个函数。比如下面的Swap函数就可以根据参数类型来判断该调用哪一Swap函数。

    void Swap(int &a, int &b)
    {
        int c = a;
        a = b;
        b = a;
    }
    
    void Swap(double &a, double &b)
    {
        double c = a;
        a = b;
        b = a;
    }
    
    ....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    虽然函数重载很方便,但是仍然有一些问题:

    1. 重载的函数仅仅是类型不同,代码复用率低,一旦出现新类型,就需要用户自己增加。
    2. 代码的可维护性低,一个重载出错可能导致所有的重载出错。

    那么我们可不可以写一套所有类型通用的模具,就像印刷机的模板一样,当我们传入不同参数时,编译器会依照这个模板自动来生成对应的代码呢?

    1653034188517

    在C++中添加了:函数模板和类模板。他们便是我们实现上述懒人方法的关键。

    函数模板

    函数模板格式

    函数模板格式如下:

    template<typename T1,typename T2...>
    返回值 函数名(参数列表)
    {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. template是C++中的关键字,用来声明某个模块为模板。
    2. typename是用来定义模板参数关键字,也可使用class替代(不可使用struct)。
    3. T1,T2…为类型名可用其他字符进行替换。

    在这里呢,我先写一个Swap,交换函数的模板,来看看实际该怎么用。

    template<typename T>
    void Swap(T&a,T&b)
    {
        T c = a;
        a = b;
        b = c;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    函数模板原理

    函数模板是一个印刷版,它本身并不是一个函数。当我们去使用这个模板的时候,编译器会根据我们的传递的参数的类型,在编译时自动去生成一个对应的函数来供我们使用。

    比如下面的一个加法的模板:

    image-20220628144027429

    当我们去调用这个模板函数时,使用的并不是它本身,而是编译器根据传入参数的类型而推演出来的下面两个函数。

    image-20220628144303663

    模板示例化

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

    1.隐式实例化

    就是让编译器根据传递的参数类型自己去推演模板的类型。我们刚刚写的加法就是如此,我们没有指定T的类型,而是由编译器自己去推演。

    image-20220628144027429

    2.显式实例化

    我们自己在函数名后的<>指定模板参数的类型称为显式实例化。

    image-20220628151457323

    在上面这段代码中我,如果我们不对函数进行显式实例化的化就会发生报错,因为a,b为两个不同类型的参数,编译器无法通过推演来判断T的类型,而我们使用显式实例化给T声明了类型,此时再调用Add函数时编译器会将T看做int型,而传入的非int型的b也会进行类型转换之后再传递给Add函数。

    模板参数匹配原则

    1. 一个非模板函数可以和一个同名的模板函数同时存在,而且该函数模板还可以被实例化为这个非模板函数

      int Add(int a, int b)
      {
          return a + b;
      }
      
      template<typename T>
      T Add(T a, T b)
      {
          return a + b;
      }
      
      int main()
      {
          int a = 1, b = 2;    
          Add(a, b);//此时的调用的Add函数为我们自己所写的Add()
          Add<int>(a, b);//为编译器根据模板自动生成的
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

      int Add(int a, int b)
      {
          return a + b;
      }
      
      template<typename T1, typename T2>
      T1 Add(T1 a, T2 b)
      {
          return a + b;
      }
      
      int main()
      {  
          Add(1, 2);//此时的调用的Add函数为我们自己所写的Add()
          Add(1, 2.0);//模板可以生成更加适配的版本,使用使用模板生成的函数
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

    类模板

    类模板格式

    类模板格式如下:

    template<class T1, class T2, class T3...>
    class 类模板名
    {
    };
    
    • 1
    • 2
    • 3
    • 4

    这里使用STL中的vector容器的部分作为示范,vector其实就是我们以前在用C语言实现的动态顺序表。

    template <class T>
    class vector
    {
    public:
        vector(size_t capacity = 10)
            : _pDate(new T[capacity]),
              _size(0),
              _capacity(capacity)
        {
        }
    
    private:
        T *_pDate;
        size_t _size;
        size_t _capacity;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们可以通过改变T的类型来改变vector容器中存储元素的类型。

    类模板实例化

    类模板实例化与函数模板实例化不同,之前我们可以让编译器自己去推断参数类型,但是类模板无法这样做,必须由我们自己去声明类型

    vector<int> s1;//存储int类型的容器s1
    vector<double> s2; //存储double的容器s2;
    
    • 1
    • 2

    结语

    1647941444633

    欢迎各位参考与指导!!!

  • 相关阅读:
    postgres 多面、多线转换成单线或者单面
    Soot(一):Soot的三种使用方式:命令行、idea、eclipse
    使用PostGIS对数据做拓扑抽稀
    通过ORPO技术微调 llama3大模型(Fine-tune Llama 3 with ORPO)
    java计算机毕业设计Web网上购书后台管理系统MyBatis+系统+LW文档+源码+调试部署
    docker容器安装MySQL,navicat无法连接报错(10060/10061错误)
    MQTT Qt 客户端开发记录
    软件测试月薪10K如何涨到30K,只有自动化测试能做到
    小程序微信支付功能逻辑
    MySQL实践总结-
  • 原文地址:https://blog.csdn.net/m0_60447315/article/details/125509416