泛型编程
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 #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种方法的优点(1. 代码复用,适合所有类型; 2. 类型检查)呢?--- 当然有了,那就是 函数模板。
2、函数模板的定义
所谓函数模板,实际上是建立一个通用函数,它所用到的数据类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位);当发生函数调用时再根据传入的实参来自动推导出真正的数据类型。
在函数模板中,数据的值和类型都被参数化了,发生函数调用时编译器会根据传入的实参来推演形参的值和类型。换个角度说,函数模板除了支持值的参数化,还支持类型的参数化。
只要定义了函数模板,就可以将类型参数用于函数定义和函数声明了。
3、函数模板的特点
1. 在函数定义时可以不指明具体的数据类型;--- 函数定义
2. 一种特殊的函数,可用不同的类型进行调用;--- 函数调用
3. 看起来与普通函数很相似,区别是类型被参数化(当发生函数调用时,数据的类型可以通过参数来传递,编译器根据传入的实参自动推断数据类型)。--- 参数传递
4、函数模板的语法规则
template <typename T>
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、
以上是关于泛型编程的主要内容,如果未能解决你的问题,请参考以下文章