泛型编程

Posted nbk-zyc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了泛型编程相关的知识,希望对你有一定的参考价值。

目录

  函数模板

  类模板

   顾名思义泛型编程就是使用“泛型”的思想去写代码,这里的“泛型”指的是将数据类型作为参数传递(类型参数化);换言之 泛型编程 是 一种不考虑具体数据类型的编程方式,其典型代表就是STL(Standard Template Library 标准模板库)。

  如果 将泛型编程的思想应用于面向对象中,就会产生一个“模板”;

 

  1. 将泛型编程的思想应用于函数中,就产生了函数模板(通用函数);

  2. 同理,将泛型编程的思想用于类中,就会产生类模板(通用类);

  接下来就分别介绍这两种技术:函数模板、类模板。

函数模板(Function Template)

1、函数模板的来源

  为了更加深刻的理解函数模板,我们可以用一个例子说明情况。比如,现在要交换两个变量的值,怎么做?就目前来看有2种方法,分别是 使用宏代码块 和 使用函数定义; 

技术图片
 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 #define SWAP(t, a, b)     7 do                        8 {                         9     t c = a;             10     a = b;               11     b = c;               12 }while(0)
13 
14 int main()
15 {
16     int a = 10;
17     int b = 11;
18     
19     SWAP(int, a, b);
20     
21     cout << "a = " << a << endl;
22     cout << "b = " << b << endl;
23     
24     double m = 12;
25     double n = 13;
26     
27     SWAP(double, m, n);
28     
29     cout << "m = " << m << endl;
30     cout << "n = " << n << endl;
31     
32     string s1 = "c++";
33     string s2 = "python";
34     
35     SWAP(string, s1, s2);
36     
37     cout << "s1 = " << s1 << endl;
38     cout << "s2 = " << s2 << endl;
39     
40     return 0;
41 
42 }
43 /**
44 * a = 11
45 * b = 10
46 * m = 13
47 * n = 12
48 * s1 = python
49 * s2 = c++
50 */
方法1:使用宏代码块
技术图片
 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 void Swap(int& a, int& b)
 7 {
 8     int c = a;
 9     a = b;
10     b = c;
11 }
12 
13 void Swap(double& a, double& b)
14 {
15     double c = a;
16     a = b;
17     b = c;
18 }
19 
20 void Swap(string& a, string& b)
21 {
22     string c = a;
23     a = b;
24     b = c;
25 }
26 
27 
28 int main()
29 {
30     int a = 10;
31     int b = 11;
32     
33     Swap(a, b);
34     
35     cout << "a = " << a << endl;
36     cout << "b = " << b << endl;
37     
38     double m = 12;
39     double n = 13;
40     
41     Swap(m, n);
42     
43     cout << "m = " << m << endl;
44     cout << "n = " << n << endl;
45     
46     string s1 = "c++";
47     string s2 = "python";
48     
49     Swap(s1, s2);
50     
51     cout << "s1 = " << s1 << endl;
52     cout << "s2 = " << s2 << endl;
53     
54     return 0;
55 
56 }
57 /**
58 * a = 11
59 * b = 10
60 * m = 13
61 * n = 12
62 * s1 = python
63 * s2 = c++
64 */
方法2:使用函数定义

     通过案列可知,这2种方法都可以实现功能,但是它们各自都有缺点。

    定义宏代码块

      优点:代码复用,适合所有的类型;

      缺点:缺少类型检查;

    定义函数

      优点:真正的函数调用,有类型检查;

      缺点:根据类型重复定义函数,无法代码复用;

  那么,有没有一种方法可以同时拥有上述2种方法的优点(1. 代码复用,适合所有类型; 2. 类型检查)呢?--- 当然有了,那就是 函数模板

2、函数模板的定义

  所谓函数模板,实际上是建立一个通用函数,它所用到的数据类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位);当发生函数调用时再根据传入的实参来自动推导出真正的数据类型。

  在函数模板中,数据的值和类型都被参数化了,发生函数调用时编译器会根据传入的实参来推演形参的值和类型。换个角度说,函数模板除了支持值的参数化,还支持类型的参数化

  只要定义了函数模板,就可以将类型参数用于函数定义和函数声明了。

3、函数模板的特点

  1. 在函数定义时可以不指明具体的数据类型;--- 函数定义

  2. 一种特殊的函数,可用不同的类型进行调用;---  函数调用

  3. 看起来与普通函数很相似,区别是类型被参数化(当发生函数调用时,数据的类型可以通过参数来传递,编译器根据传入的实参自动推断数据类型。--- 参数传递

4、函数模板的语法规则

  template <typename T>

  template <typename T, typename T1, typename T2, ...>  多参数的函数模板, T 第一个类型参数,T1 是第二个类型参数...

  template 关键字用于声明开始进行泛型编程

  typename 关键字用于声明泛指类型

  技术图片

技术图片
 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 
 5 template < typename T >
 6 void Swap(T& a, T& b)
 7 {
 8     T c = a;
 9     a = b;
10     b = c;
11 }
12 
13 template < typename T >
14 void Sort(T a[], int len)
15 {
16     for(int i=0; i<len; i++)
17     {
18         for(int j=i; j<len; j++)
19         {
20             if( a[i] > a[j] )
21             {
22                 Swap(a[i], a[j]);
23             }
24         }
25     }
26 }
27 
28 template < typename T >
29 void Print(T a[], int len)
30 {
31     for(int i=0; i<len; i++)
32     {
33         cout << a[i] << ", ";
34     }
35 
36     cout << endl;
37 }
38 
39 int main()
40 {
41     int a[5] = {5, 3, 2, 4, 1};
42 
43     cout << "排序前:";
44     Print(a, 5);
45     Sort(a, 5);
46     cout << "排序后:";
47     Print(a, 5);
48 
49     string s[5] = {"Java", "C++", "Python", "html", "Matlab"};
50 
51     cout << "排序前:";
52     Print(s, 5);
53     Sort(s, 5);
54     cout << "排序后:";
55     Print(s, 5);
56 
57     return 0;
58 }
59 /**
60  * 排序前:5, 3, 2, 4, 1, 
61  * 排序后:1, 2, 3, 4, 5, 
62  * 排序前:Java, C++, Python, Html, Matlab, 
63  * 排序后:C++, Html, Java, Matlab, Python,
64 */
函数模板 实现 选择排序算法

 5、函数模板的调用方式

  !!!发生函数调用时,函数模板会根据 实参   对  参数类型   进行推导,但无法自动推导返回值类型

  !!!函数模板本身不允许隐式类型转换

  1. 自动推导调用方式(必须严格匹配,不能进行隐式类型转换);

  2. 具体类型显示调用(显示的指定数据类型,可以进行隐式类型转换)

  3. 自动推导 + 显示调用 二者结合(在多参数的模板函数中使用);

   技术图片

6、多参数的函数模板

  可以从左向右部分指定类型参数,工程中将返回值类型作为第一个类型参数(这样方便进行自动推导); 

  技术图片

技术图片
 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 template 
 7 < typename T1, typename T2, typename T3 >
 8 T1 Add(T2 a, T3 b)
 9 {
10     return static_cast<T1>(a + b);
11 }
12 
13 int main()
14 {
15     // T1 = int, T2 = double, T3 = double
16     int r1 = Add<int>(0.5, 0.8);
17 
18     // T1 = double, T2 = float, T3 = double
19     double r2 = Add<double, float>(0.5, 0.8);
20 
21     // T1 = float, T2 = float, T3 = float
22     float r3 = Add<float, float, float>(0.5, 0.8);
23 
24     cout << "r1 = " << r1 << endl;     // r1 = 1
25     cout << "r2 = " << r2 << endl;     // r2 = 1.3
26     cout << "r3 = " << r3 << endl;     // r3 = 1.3
27     
28     return 0;
29 }
多参数的函数模板

 7、函数模板与函数重载

  调用规则:

  1. 函数模板可以像普通函数一样被重载;

  2. C++编译器优先考虑普通函数;

  3. 如果函数模板可以产生一个更好的匹配,那么选择模板;

  4. 可以通过 空模板实参列表 的语法限定编译器只通过模板匹配 。// func<>(a,b);

  !!!函数模板本身不允许隐式类型转换

技术图片
 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 
 7 template < typename T >
 8 T Max(T a, T b)
 9 {
10     cout << "T Max(T a, T b)" << endl;
11     
12     return a > b ? a : b;
13 }
14 
15 int Max(int a, int b)
16 {
17     cout << "int Max(int a, int b)" << endl;
18     
19     return a > b ? a : b;
20 }
21 
22 template < typename T >
23 T Max(T a, T b, T c)
24 {
25     cout << "T Max(T a, T b, T c)" << endl;
26     
27     return Max(Max(a, b), c);
28 }
29 
30 int main()
31 {
32     cout << Max(1, 2) << endl;          // 普通函数 Max(int, int)
33     cout << Max<>(1, 2) << endl;        // 函数模板 Max<int>(int, int)
34     cout << Max(3.0, 4) << endl;        // 普通函数 Max(int, int)
35     cout << Max(3.0, 4.0) << endl;      // 函数模板 Max<double>(double, double)
36     cout << Max(5.0, 6.0, 7.0) << endl; // 函数模板 Max<double>(double, double, double)
37     
38     return 0;
39 }
40 /**
41  * int Max(int a, int b)
42  * 2
43  * T Max(T a, T b)
44  * 2
45  * int Max(int a, int b)
46  * 4
47  * T Max(T a, T b)
48  * 4
49  * T Max(T a, T b, T c)
50  * T Max(T a, T b)
51  * T Max(T a, T b)
52  * 7
53 */
函数模板与函数重载

8、函数模板的实现机制

  1. 编译器从函数模板通过具体类型产生不同的函数;

  2. 编译器会对函数模板进行两次编译

 (1)对模板代码本身进行编译;

 (2)对参数替换后的代码进行编译;

技术图片
 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Test
 7 {
 8     //Test(const Test&);
 9 public:
10     Test()
11     {
12     }
13 };
14 
15 template < typename T >
16 void Swap(T& a, T& b)
17 {
18     T c = a;  // 当T=Test时,第二次编译就会调用拷贝构造函数(private),此时编译失败
19     a = b;
20     b = c;
21 }
22 
23 typedef void(FuncI)(int&, int&);
24 typedef void(FuncD)(double&, double&);
25 typedef void(FuncT)(Test&, Test&);
26 
27 int main()
28 {
29     FuncI* pi = Swap;    // 编译器自动推导 T 为 int
30     FuncD* pd = Swap;    // 编译器自动推导 T 为 double
31     FuncT* pt = Swap;    // 编译器自动推导 T 为 Test
32     
33     cout << "pi = " << reinterpret_cast<void*>(pi) << endl; // pi = 0x400a0a
34     cout << "pd = " << reinterpret_cast<void*>(pd) << endl; // pd = 0x400a37
35     cout << "pt = " << reinterpret_cast<void*>(pt) << endl; // pt = 0x400a70
36     
37     return 0;
38 }
函数模板的本质(通过函数指针测试)

 

  

类模板

1、 类模板的概念和意义

  在STL中有很多类(数组类、链表类、Stack 类、Queue 类等),这些类主要用于存储和组织数据元素,且这些类的实现与具体的数据类型无关;这样做的好处是将统一的算法应用在不同的数据类型之间。那么,在STL中,这项技术是如何实现的?--- 通过 类模板 实现。

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

2、 

 

以上是关于泛型编程的主要内容,如果未能解决你的问题,请参考以下文章

这个嵌套类构造函数片段可以应用于泛型类吗?

泛型编程

链式编程:泛型实现的扩展方法类

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

Golang泛型编程初体验