一、如何进行泛型编程
-
C/C++是一种静态编程语言,必须需要把代码翻译成可执行的二进制可执行程序然后再运行,一旦编译好之后就不能再变了(数据类型也必须确定下无法更改,因此要为每一种数据类型编写一份算法,工程量巨大)
-
C语言中的快速排序
void qsort(void base, size_t nmemb, size_t size,int(compar)(const void , const void )); .#include#include <stdio.h> #include <stdlib.h> int intcmp(const void* p_1,const void* p_2) { const char* p1 = p_1; const char* p2 = p_2; if(p1 == p2) return 0; else if(p1 > p2) return 1; else return -1; } int main() { char arr[10] = {9,6,3,1,2,8,4,0,5,7}; qsort(arr,10,sizeof(arr[0]),intcmp); for(int i=0; i<10; i++) { printf("%d ",arr[i]); } printf(" "); }
- C++提供了模版的编程方法来解决泛型编程的问题,它的解决思路是,程序员先编写好一份"套路"代码,然后在调用时编译器根据调用时的参数再为这种数据类型生成一份属于它的代码
#include <iostream> using namespace std; template T bubble_sort(T* arr,M size) { for(int i=size-1; i>=0; i--) { bool flag = true; for(int j=0; j<i; j++) { if(arr[j] >arr[j+1]) { swap(arr[j],arr[j+1]); flag = false; } } //如果这一趟数据没有发生交换,说明数据已经排序完成 if(flag) return; } } int main() { char arr[10] = {'5','2','6','3','8','9','1','0','7','4'}; pop_sort(arr,10); for(int i=0; i<10; i++) { cout << arr[i]; } }
二、模版的语法
template T max(T num1,T num2) { return num1>num2?num1:num2; }
- a、模版的类型参数
T被称为模版的类型参数,可以叫任何名字,只是俗称约定叫做T
它指的是函数调用时的任何类型的参数
虽然模版的类型可以是任意的,但是必须要支持模版函数中所使用到的运算符,因此模版不是万能的,虽然能带来很多好处,但也带来了很多问题
三、函数模版的使用
- 1、模版的实例化
编译器不会把函数模版编译成一个实例,而是根据调用时的参数,再进行实例化(进一步生成二进制指令) - 2、使用模版时才实例化
模版只有在调用时才会实例化,因此模版编译正确并非代码没有问题,很多错误会产生于调用时 - 3、二次编译
第一次是检查模版的语法,第二次编译是根据调用参数把模版实例化出来然后再检查运算符是否支持这种类型
四、函数模版的隐式推断
- a、使用函数模版时可以根据参数的类型来推断模版的类型来推断模版的参数
- b、当函数模版不能通过函数调用时的参数来推断模版参数时,可以使用<类型,类型..>来明确指定
- c、函数模版参数可以有默认值
- 1、默认值优先放在右边
- 2、C++标准才支持:-std=C++0x
五、函数模版与普通函数可以重载
- 1、同一种格式的函数和函数模版是可以共生的,但优先调用普通函数,但可以有函数名后添加一个空的<>指定调用函数模版,模版参数根据调用时的参数推断
- 2、普通函数在调用是可以进行类型提升,但是这种提升的结果要低于模版的实例化
- 3、函数模版也可以进行类型提升,但如果有一个普通函数也可以进行类型提升调用,那么则优先调用普通函数
#include <iostream> using namespace std; short Max(const short num1,const short num2) { cout << "func" << endl; return num1>num2?num1:num2; } template T Max(const T num1,const T num2) { cout << "template" << endl; return num1>num2?num1:num2; } int main() { short num1 =20,num2=30; cout << Max<>(num1,num2) << endl; cout << Max(num1,num2) << endl; }
六、类模版
-
a、类模版的语法
类模版的参数可以在类中当作类型使用,可以定义成员、返回值、参数等template class className { C c public: T func(A a); };
注意:typename也可以继续使用,但大多用class以示区别 -
b、类模版的使用
类模版必须要经过实例化才能使用,也是需要经过两次编译,第一次是把类模版编译成一个"套路",这个过程是为了检查语法,第二次是根据实例化参数,生成一个类,然后才能使用这个类创建对象使用类模版实例化一个类
className a;#include <iostream> using namespace std; template class A { T t; public: A(T _t):t(_t) { cout << "我是构造函数" << endl; } T& get_t(void) { return t; } void set_t(T _t) { t = _t; } void show(void) { cout << t << endl; } }; int main() { A a(20); a.set_t(30); a.show(); cout << a.get_t() << endl; }
-
c、类模版参数不支持隐式推断,必须显示实例化
-
d、静态成员的定义
template int MyStack::num = 10;
静态成员必须在类模版实例化之后才被真正的定义出来,每个实例化的类都有一份静态成员,这个实例化类创建出的对象共用一个静态变量 -
e、递归实例化
MyStack<MyStack> stack;
七、类模版的特化(重载)
特化:指的是当类模版有特殊的类型无法处理时,可以为这种特殊类型单独实例化出一个类,这种单独的实现叫做模版的特化
- 全类的特化:按照类的格式把类完整再实现一遍(重写一遍)
template <> class className
{
...
}; - 成员特化:给指定的类型提供一个特殊的成员函数
template<> 返回值类型 className::max(void)
{
...
} -
局部特化:可以让用户根据实例化时的参数来指定使用的类模板
template class N
{public:N(void){cout <<"1"<<endl;}};
template class N
{public:N(void){cout <<"2"<<endl;}};
template class N
{public:N(void){cout <<"3"<<endl;}};
template class N
{public:N(void){cout <<"4"<<endl;}};
template class N
{public:N(void){cout <<"5"<<endl;}};注意:同等程序的特化会引起二义性。
八、类模板的参数
- 1、类模板的参数可以有默认值
注意:使用类模板默认值时,<>不能省略,可以空着,但不能不写。 -
2、类模板后面的参数可以调用前面的参数。
-
3、普通数值可以对类模板进行实例化,它必须以类似变量的形式存在。
注意:只能是常量才能进行实例化。#include <iostream> using namespace std; template R Max(T num1,T num2) { return num1>num2?num1:num2; } int main() { // 错误调用 // cout << Max(10,30.14) << endl; char ch = 100; cout << Max(10,ch) << endl; }