博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++的泛型编程与模板
阅读量:3739 次
发布时间:2019-05-22

本文共 2656 字,大约阅读时间需要 8 分钟。

c++的泛型编程与模板

“泛型编程”:一种不考虑具体数据类型的编程方式

1.用泛型编程实现类模板

假如现在有个需求,我们要实现一些类,主要用于存储和组织数据结构(如链表类、堆类等),关注其实现的裸机,而不是数据结构中元素的具体类型

  • 使用泛型编程,即实例化类时,向其指定数据类型。这样实现的类十分的精简高效,但其实现本质是根据参数不同定义了不同的类,可以认为类模板是一种语法糖
  • 类模板无法像函数模板那样自动推导类型,只能显示指定类型
template //类模板一般在头文件里定义,template关键字告诉编译器开始泛型编程,< typename T1,  typename T2, int N>//typename关键字用于声明泛指类型class Operator{public:    T1 add(T2 a, N)//模板的预处理阶段可以传入参数,只能传递常量,有点类似于宏定义,N就是一个参数};template //template关键字告诉编译器开始泛型编程< typename T1,  typename T2, int N>//如果成员函数在外面定义,则也要加template和typename T1  Operator
::add(T2 a, N){ return static_cast
(a + b);}int main(){ Operator
op1; //实例化类时,必须显示指定类型 op1.add(1, 2); //调用类中的成员函数 return 0;}
  • 类模板是可以特化的,所谓特化,就是对类模板的某一种情况进行定义,当满足该情况时就会优先调用这个特化模板。有点类似于重载,但重载是差异化的,特化是特殊化的
template//原模板< typename T1, typename T2 >class Test{public:    void add(T1 a, T2 b)    {        cout << "void add(T1 a, T2 b)" << endl;    }};template< typename T >class Test < T, T >    // 当 Test 类模板的两个类型参数完全相同时,使用这个实现{public:    void add(T a, T b)//特化的模板可重写原模板的函数    {        cout << "void add(T a, T b)" << endl;    }    void print()//也可以新增原模板的函数    {    }};template<  >class Test < void*, void* >    //完全特化,当参数都为void*时优先调用{public:    void add(void* a, void* b)    {        cout << "void add(void* a, void* b)" << endl;    }};int main(){      Test
t1;//调用原模板 Test
t2;//调用部分特化的模板 Test
t3;//调用完全特化的模板 return 0;}

2.用泛型编程实现函数模板

假设现在有个需求,需要我们实现一个Add接口,该接口可以对两个类型(int、double、string等都有可能)的变量进行相加,并将结果强制类型转换为特定类型再回

  • 那么问题来了,这个函数对于参数的类型很讲究,但是参数类型我们事先是不知道的,那么只能对所有情况进行函数重载。但是这些重载函数仅仅是参数不同,内部逻辑是完全相同的,十分冗余
  • 可以使用“泛型编程”,即类型也作为参数传入。这样实现的Add函数十分的精简高效,但其实现本质仍是函数重载,可以认为函数模板是函数重载的语法糖
template //template关键字告诉编译器开始泛型编程< typename T1,  typename T2, typename T3>//typename关键字用于声明泛指类型T1 Add(T2 a, T3 b){    return static_cast
(a + b);}
  • 此外,函数模板还可以进行“完全特化”,与类模板不同,函数模板不能进行“部分特化”。所谓特化,就是对函数模板的某一种情况进行定义,当满足该情况时就会优先调用这个特化模板。有点类似于重载,但重载是差异化的,特化是特殊化的
template//Add函数模板的一个完全特化< >char Add(int a, float b){    cout << "123" << endl;//特化的函数模板可以和原模板不一样    return static_cast
(a + b);}
  • 函数模板有两种使用方式,自动推导和显式指明,下面以swap为例
    • 若使用自动推导方法,则无法推导出返回值的类型
    • 若使用显式指明,则指明的类型按照之前template < typename T1, typename T2, typename T3>中的顺序一一对应,工程中一般将第一个类型作为返回值类型
int a = 1;float b = 0;char c = Add(b, a);//调用原模板,自动推导参数类型,但是无法推导返回值的类型char d = Add
(a, b);//调用完全特化模板,显式指明类型为T1为char,T2为int,T3为float,char d = Add
(a, b);//当然,也可以偷个懒,只指明返回值类型为char,反正参数类型可以自动推导....

3.普通函数与函数模板的冲突

函数模板也能被重载,不过只根据参数的个数不同来重载罢了,并且工程中,一般只是特化函数模板,并不会去重载函数模板。那么当一个普通的函数及其重载函数,遇到一个同名的函数模板及其特化时,编译器优先调用谁?

  • 当参数匹配时,调用优先级:普通函数及其重载函数>函数特化模板>函数模板
  • 调用时可以使用空的模板实参列表,如Add<>(a, b)来指定编译器调用函数模板

转载地址:http://lpvin.baihongyu.com/

你可能感兴趣的文章
项目阶段二:代码优化(servlet程序和一些工具类)
查看>>
SpringBoot里MVC自动配置原理
查看>>
员工管理系统一:数据准备和配置项目环境
查看>>
IDEA连接mysql又报错设置时区!Server returns invalid timezone.
查看>>
员工管理系统二:首页和国际化实现
查看>>
员工管理系统三:登录+拦截器
查看>>
员工管理系统四:员工列表实现
查看>>
员工管理系统五:增删改员工实现
查看>>
员工管理系统六:404及注销
查看>>
SpringBoot的定制错误数据
查看>>
项目阶段三:图书模块的数据库准备与dao、Service层
查看>>
项目阶段三:图书模块的增删改查
查看>>
MySQL的安装与卸载
查看>>
Redis的安装与卸载
查看>>
项目阶段四:分页模块
查看>>
项目阶段四:首页的分页
查看>>
项目阶段五:登录登出优化
查看>>
项目阶段五:验证码
查看>>
项目阶段五:购物车
查看>>
项目阶段六:订单模块的数据库准备与dao、service层
查看>>