C++——模板---函数模板---类模板

一、模板

  • 模板的引入:
    • 模板的精神:类型参数化,即类型也是一种参数。
    • template所代表的泛型编程是C++语言中的重要组成部分。C++是一门强类型语言,无法像动态语言(如python)那样,编写一段通用的逻辑,可以把任意类型的变量传进去。泛型编程弥补了这一点摆脱了类型的限制,提高了代码的可重用性。
    • 模板是建立通用的与数据类型无关的算法的重要手段,可实现代码重用。
  • 函数模板的定义与声明:---函数模板实参推演
    • 定义:
      • template<模板参数表>返回值类型 函数名(函数形参表){......}//函数体
      • 模板参数主要是模板类型参数,尖括号中不能为空。模板类型参数由typename(或class)+标识符构成。表示该标识符代表一种潜在的内置或用户自定义数据类型。
    • 声明:
      • 函数模板的声明与函数声明不同,函数模板的声明必须含变量名。因为两者的编译过程不一样。函数模板必须先转换成模板函数,再进行编译。模板定义本身不参与编译,而是编译器模板的用户使用模板时提供的类型参数生成代码,再进行编译。这一过程成为模板的实例化。用户提供不同的类型参数就会实例化出不同的代码。
  • 类模板的定义与声明:---类模板实例化
    • 定义:
      • template<模板参数表> class 类名
        {
        //类定义体
        };//注意分号不可少
        
        templete<模板参数表>返回类型 类名<模板参数名表>::
        成员函数名1(形参表)
        {
        ......;//成员函数定义体
        }

         模板参数表有两种:模板类型参数和非模板类型参数。




 

二、函数模板---template关键字用于声明开始进行泛型编程-----typename关键字用于声明泛指类型----函数模板可以自动推导类型进行调用,也可以显示指定具体类型进行调用。

  1、普通函数模板:

    •  1 template<typename T>
       2 int compare(const T& left,const T& right)
       3 {
       4 if(left<rigth)
       5 {return -1;}
       6 if(right<left)
       7 {return 1;}
        reurn 0;
      8 } 9 10 compare<int>(1,2);//使用模板函数

  2、类的成员函数模板

不仅普通函数可以定义为模板,类的成员函数也可以定义为模板

    • class printer
      {
      public:
      template<typename T>
      void print(const T& t)
      {
      cout<<t<<endl;
      }
      }
      
      printer p;
      p.print<const char*>("seu");//打印seu

  • 实参推断:

为了使用方便,除了直接为函数模板指定类型参数外,还可以让编译器从传递给函数的实参推断参数类型,这一功能被称为模板实参推断。

  • 实参推断的使用:
    • 1 compare(1,2);//推断T的类型为int
      2 compare(1.0,2.0);//推断T的类型为double
      3 p.print("absdsaf");//推断T的类型为const char *
      4 
      5 int (*pf)(const int&,const int&)=compare;//推断T的类型为int;
      6 //通过把函数模板赋值给一个指定类型的函数指针,让编译器根据这个指针的类型,对模板实参进行判断

 

  3、函数模板的重载

函数模板之间,函数模板与普通函数之间可以重载。编译器会根据调用时提供的函数参数,调用能够处理这一类型的最特殊的版本。在特殊性上,一般遵循如下顺序:

  • 普通函数
  • 特殊函数(限制了T的形式的,指针,引用,容器等)
  • 普通模板(对T没有任何限制的) 
  • 如何判断哪个模板函数更加特殊,原则是:如果模板B的所有实例都可以实例化模板A,而反过来不行,那么B就比A特殊。即B的范围大于A的范围。
    •  1 template<typename T>
       2 void func(T& t) { //通用模板函数
       3     cout << "In generic version template " << t << endl;
       4 }
       5 
       6 template<typename T>
       7 void func(T* t) { //指针版本
       8     cout << "In pointer version template "<< *t << endl;
       9 }
      10 
      11 void func(string* s) { //普通函数
      12     cout << "In normal function " << *s << endl;
      13 }
      14 
      15 int i = 10;
      16 func(i); //调用通用版本,其他函数或者无法实例化或者不匹配
      17 func(&i); //调用指针版本,通用版本虽然也可以用,但是编译器选择最特殊的版本
      18 string s = "abc";
      19 func(&s); //调用普通函数,通用版本和特殊版本虽然也都可以用,但是编译器选择最特化的版本
      20 func<>(&s); //调用指针版本,通过<>告诉编译器我们需要用template而不是普通函数

  4、模板函数的特化

有时候函数模板并不能解决个别类型的问题,我们必须对此进行定制,这就是函数模板的特化。函数模板的特化必须把所有的模板参数全部指定。

  • 1 template<>
    2 void func(int i) {
    3     cout << "In special version for int "<< i << endl; 
    4 }
    5 
    6 int i = 10;
    7 func(i); //调用特化版本



 

 

三、类模板---

类模板只能显示指定类型参数,无法自动推断参数类型。声明的泛型类型参数可以出现在类模板的任意地方。类模板必须在头文件中实现,不能分开实现在不同的文件中。

类模板适合以相同的逻辑处理不同数据类型的数据,因此非常适合编写数据结构相关的代码。通常用来作为容器(vector)或行为(clonable)的封装。

对于一个类模板printer,只能显示的指定参数类型进行调用

  • printer<int> p(1);正确
    printer p(1);错误

     

     

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-01-13 17:28  long_ago  阅读(586)  评论(0编辑  收藏  举报