轻松理解泛型编程

泛型编程简介

  • 泛型编程的概念
    • C++中泛型编程
    • 1.函数模板
    • 2.类模板
    • 总结

泛型编程的概念

什么是泛型编程?
–不考虑具体数据类型的编程方式
泛型编程的意义:
–泛型编程就是为了代码复用! 是C++中重要的代码复用方式

建议:阅读本文时先总看框架,再分看细节,这样效果比较好。

例: 对于Swap函数可以考虑下面的泛型写法
     void Swap(T& a, T& b)
     {
     	T t = a;
     	a = b;
     	b = t;
     }
     此泛型写法中的T不是具体的数据类型,而是泛指任意的数据类型。

C++中泛型编程

1.函数模板

--是一种特殊的函数可以用不同类型进行调用
--看起来和普通函数很相似,区别是类型可被参数化

函数模板的语法规则

-template 关键字用于声明开始进行泛型编程
-typename  关键字用于声明泛指类型
例:
 template 
 void Swap(T& a, T& b)
 {
 	T t = a;
 	a = b;
 	b = t;
 }

函数模板的使用

--自动类型推导调用
--具体类型显示调用
  例:
    int a = 0;
    int b = 1;
    Swap(a, b);    //自动推导
    float c = 2;
    float d = 3;
    Swap(c, d);  //显示调用

函数模板深入理解

-编译器从函数模板通过具体类型产生不同的函数,然后对函数模板进行两次编译
  1,对模板代码本身进行编译
  2,对参数替换后的代码进行编译

注意事项:

-函数模板本身不允许隐式类型转换
  自动推导类型时必须严格匹配
  显示类型指定时,能够进行隐式类型转换   !!!建议显示调用

函数模板可以定义任意多个不同的类型参数

对于多参数函数模板,无法自动推导返回值类型,可以从左向右部分指定类型参数
template < typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
    return static_cast(a + b);
}

 int r1 = Add(0.5, 0.8);                  //T1 = int, T2 = double, T3 = double
 int r2 = Add(0.5, 0.8);           //T1 = int, T2 = float, T3 = double
 int r3 = Add(0.5, 0.8);    // T1 = int, T2 = float, T3 = float
 
 注: 工程中将返回值作为第一个类型参数!! 返回值类型必须显示指定。

函数重载与函数模板

函数模板可以像普通函数一样被重载
   -C++编译器优先考虑普通函数
   -如果函数模板可以产生一个更好的匹配,那么选择模板
   -可以通过空模板实参列表限定编译器只匹配模板
   例:
     int r1 = Max(1, 2);
     double r2 = Max<>(0.5, 0.8);
                  限定编译器只匹配函数模板

2.类模板

一些类主要用于存储和组织数据元素,类中数据组织的方式和数据元素的具体类型无关
如:数组类、链表类、Stack类、Queue类,等

C++中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。

C++中的类模板

-以相同的方式处理不同的类型
-在类声明前使用template进行标识
-用于说明类中使用的泛指类型T
例:
   template < typename T >
   class Operator
   {
   public:
       T op(T a, T b);
   };

类模板的应用

-只能显示指定具体类型,无法自动推导
-使用具体类型定义对象
例:
     Operator op1;
     Operator op2;
     int i = op1.op(1, 2);
     String s = op2.op("yang", "wen");

注意:
声明的泛指类型 T 可以出现在类模板的任意地方
编译器对类模板的处理方式和函数相同

-从类模板通过具体类型产生不同的类
-在声明的地方对类模板代码本身进行编译
-在使用的地方对参数替换后的代码进行编译

类模板的工程应用

-类模板必须在头文件中定义
-类模板不能分开实现在不同的文件中
-类模板外部定义的成员函数需要加上模板<>声明

类模板可以定义任意多个不同的类型参数

例:
   template 
   class Test
   {
   public:
   	void add(T1 a, T2 b);
   };
   
   Test t;

类模板可以被特化

特化:指定类模板的特定实现,部分类型参数必须显示指定,根据类型参数分开实现类模板
例:
  template 
  class Test
  {
  
  };
  特化后:
  template 
  class Test< T, T >    //规定的地方在这里
  {

  };
  注意观察: 特化前,参数T可以在任意地方,特化后,参数出现在了规定的地方。

类模板的特化类型

 特化类型分两种:1.部分特化:用特定规则约束类型参数,见上例。
                2.完全特化: 完全显示指定类型参数
   例:
     template 
     class Test
     {

     };
     完全特化后:
     template < >
     class Test< int, int >  //具体指定类型参数
     {

     };
     注意观察:特化前,T参数可以出现在类中任意地方,部分特化后,出现在了规定的地方,完全特化后,出现了具体的类型参数。

类模板特化注意事项

特化只是模板的分开实现,本质上是同一类模板;
特化类模板的使用方式是统一的,就是必须显示指定每一个类型参数

重定义与特化的比较

重定义:一个类模板和一个新类(或者两个类模板),使用的时候需要考虑如何选择
特化:以统一的方式使用类模板和特化类,编译器自动优先选择特化类

函数模板只能支持类型参数完全特化,而不支持部分特化

例:
  template < typename T >   //函数模板定义
  bool Equal(T a, T b)
  {
  	return a == b;
  }
  完全特化后:
  template < >
  bool Equal(void* a, void* b)
  {
	return a == b;
  }

注意:在工程中,当需要重载函数模板时,优先考虑使用模板特化;
当模板特化无法满足需求,再使用函数重载!

总结

白话理解:
        在编程写代码的时候,函数模板与类模板实际上与普通的函数和类没有什么区别,关键的区别在于:
        把具体的数据类型换成了未知的T类型,因此叫模板。 编译器在编译的时候先编译模板,实际上就是检查模板,
        然后根据使用的地方的参数来具体替换T,然后进行二次编译,二次编译与普通的编译是一样的。
        灵活的地方:就是我们可以在使用的地方任意指定参数。也是代码复用的地方。

C++中的泛型编程包括函数模板和类模板,在学习的时候,首先我们应该知道这两个存在的目的是: 代码复用! 然后要知道特性是接受不同类型的参数,因此可以想到其应用的地方在哪里,最后应该知道怎么样去使用,以及使用时应该注意的细节问题,咋一看比较复杂,实际上静下心来好好理解,反复理解,慢慢的就明白了。

注:本文由作者在狄泰软件学院学习、总结、思考所得,欢迎交流。

你可能感兴趣的:(C++)