26.C++- 泛型编程之类模板(详解)
在上章25.C++- 泛型编程之函数模板(详解) 学习了后,本章继续来学习类模板
类模板介绍
和函数模板一样,将泛型思想应用于类.
编译器对类模板处理方式和函数模板相同,都是进行2次编译
类模板通常应用于数据结构方面,使得类的实现不在关注数据元素的具体类型,而只关注需要实现的功能
比如: 数组类,链表类,Queue类,Stack类等
使用方法
- 通过template关键字来声明,然后通过typename关键字来定义模板类型,如下图所示:

类模板的使用
- 定义对象时,必须指定类模板类型,因为编译器无法推导类型
- 使用具体类型<Type>来定义对象
如下图所示:

初探类模板
写一个类模板,实现不同类型的加减乘除
#include <iostream>
#include <string> using namespace std; template < typename T >
class Operator
{
public:
T add(T a, T b)
{
return a + b;
} T minus(T a, T b)
{
return a - b;
} T multiply(T a, T b)
{
return a * b;
} T divide(T a, T b)
{
return a / b;
}
};
string operator-(string& l, string& r) //由于string类没有重载减号操作符,所以我们自定义一个
{
return "Minus";
} int main()
{
Operator<int> op1; //定义对象时,需要指定类模板类型
cout << op1.add(, ) << endl; Operator<string> op2; //定义对象时,需要指定类模板类型
cout << op2.add("D.T.", "Software") << endl;
cout << op2.minus("D.T", "Software") << endl;
return ;
}
运行打印:
D.T. Software
Minus
类模板的工程应用
- 类模板必须在.h头文件中定义
- 类模板的成员函数不能分开在不同的文件中实现
- 类模板外部定义的成员函数,和模板函数一样,还需要加上模板template <typename T>声明,以及结构体<T>声明
接下来,我们便修改上面代码定义的Operator类模板,只需要写Operator.h文件即可:
#ifndef _OPERATOR_H
#define _OPERATOR_H template < typename T >
class Operator
{
public:
T add(T a, T b);
T minus(T a, T b);
T multiply(T a, T b);
T divide(T a, T b);
}; template < typename T > //外部定义的成员函数,都需要加上模板声明
T Operator<T> :: add(T a, T b) //同时加上结构体<T>声明
{
return a+b;
} template < typename T >
T Operator<T> :: minus(T a, T b)
{
return a-b;
}
template < typename T >
T Operator<T> :: multiply(T a, T b)
{
return a*b;
} template < typename T >
T Operator<T> :: divide(T a, T b)
{
return a/b;
} #endif
多参数类模板
类模板可以定义任意多个不同的类型参数,同时还要必须指定每个模板参数
例如:
template < typename T1,typename T2 >
class Operator
{
public:
void add(T1 a, T2 b);
}; template < typename T1,typename T2 >
void Operator<T1,T2 > :: add(T1 a, T2 b)
{
cout<<a+b<<endl;
} int main()
{
Operator<int,float> op1; //定义op1对象时,必须指定类模板类型
op1.add(,2.1); //4.1
return ;
}
运行打印:
4.1
从结果来看,上面的类模板好像已经实现了add加法运算.但是却不能支持指针类型.
其实,类模板也可以像函数重载一样, 类模板通过特化的方式可以实现特殊情况.
类模板特化
- 表示可以存在多个相同的类名,但是模板类型都不一致(和函数重载的参数类似)
- 特化类型有完全特化和部分特化两种类型
- 完全特化表示显示指定类型参数,模板声明只需写成template<>,并在类名右侧指定参数,比如:
template < typename T1,typename T2 > //声明的模板参数个数为2个
class Operator //正常的类模板
{
public:
void add(T1 a, T2 b)
{
cout<<a+b<<endl;
}
}; template <> //不需要指定模板类型,因为是完全特化的类模板
class Operator< int , int> //指定类型参数,必须为2个参数,和正常类模板参数个数一致 { public: void add(int a, int b) { cout<<a+b<<endl; } }; int main() { Operator<int,int> Op1; //匹配完全特化类模板:class Operator< int,int> Operator<int,float> Op2; //匹配正常的类模板 return ; }
- 部分特化表示通过特定规则约束类型参数,模板声明和类似,并在类名右侧指定参数,比如:
template < typename T1,typename T2 > //声明的模板参数个数为2个
class Operator //正常的类模板
{
public:
void add(T1 a, T2 b)
{
cout<<a+b<<endl;
}
}; template < typename T > //有指定模板类型以及指定参数,所以是部分特化的类模板
class Operator< T* ,T*> //指定类型参数,必须为2个参数,和正常类模板参数个数一致
{
public:
void add(T* a, T* b)
{
cout<<*a+*b<<endl;
}
}; int main()
{
Operator<int*,int*> Op1; //匹配部分特化: class Operator< T* ,T*>
Operator<int,float> Op2; //匹配正常的类模板: class Operator
return ;
}
- 编译时,会根据对象定义的类模板类型,首先去匹配完全特化,再来匹配部分特化,最后匹配正常的类模板.
初探类模板特化
#include <iostream> using namespace std; template < typename T1,typename T2 >
class Operator //正常的类模板
{
public:
void add(T1 a, T2 b)
{
cout<<"add(T1 a, T2 b)"<<endl;
cout<<a+b<<endl;
}
}; template < typename T >
class Operator<T,T> //部分特化的类模板,当两个参数都一样,调用这个
{
public:
void add(T a, T b)
{
cout<<"add(T a, T b)"<<endl;
cout<<a+b<<endl;
}
}; template < typename T1,typename T2 >
class Operator<T1*,T2*> //部分特化的类模板,当两个参数都是指针,调用这个
{
public:
void add(T1* a, T2* b)
{
cout<<"add(T1* a, T2* b)"<<endl;
cout<<*a+*b<<endl;
}
}; template < >
class Operator<void*,void*> //完全特化的类模板,当两个参数都是void*,调用这个
{
public:
void add(void* a, void* b)
{
cout<<"add(void* a, void* b)"<<endl;
cout<<"add void* Error"<<endl; //void*无法进行加法
}
}; int main()
{
int *p1 = new int();
float *p2 = new float(1.25); Operator<int,float> Op1; //匹配正常的类模板:class Operator
Op1.add(,1.5); Operator<int,int> Op2; //匹配部分特化的类模板:class Operator<T,T>
Op2.add(,); Operator<int*,float*> Op3; //匹配部分特化的类模板:class Operator<T1*,T2*>
Op3.add(p1,p2); Operator<void*,void*> Op4; //匹配完全特化的类模板:class Operator<void*,void*>
Op4.add(NULL,NULL); delete p1;
delete p2; return ;
}
运行打印:
add(T1 a, T2 b)
2.5 add(T a, T b) add(T1* a, T2* b)
2.25 add(void* a, void* b)
add void* Error
数值型模板参数
之前,我们学习的模板参数都是带泛型的(表示不同类型),其实模板参数也可以是数值型参数,如下图所示:

- 数值型模板参数必须在编译时被唯一确定
比如: 变量在运行期间是可变的,所以不能作为模板参数.以及浮点数(不精确),类对象(可变)等等.
接下来,我们便通过数值参数的类模板来求 1+2+3+...+N的值
代码如下所示:
template < int N >
class Sum
{
public:
static const int VALUE = Sum<N->::VALUE + N; //定义静态常量并赋值
};
template < >
class Sum < >
{
public:
static const int VALUE = ;
}; int main()
{
cout << "1 + 2 + 3 + ... + 10 = " << Sum<>::VALUE << endl;
cout << "1 + 2 + 3 + ... + 100 = " << Sum<>::VALUE << endl;
return ;
}
运行打印:
+ + + ... + =
+ + + ... + =
26.C++- 泛型编程之类模板(详解)的更多相关文章
- 25.C++- 泛型编程之函数模板(详解)
本章学习: 1)初探函数模板 2)深入理解函数模板 3)多参函数模板 4)重载函数和函数模板 当我们想写个Swap()交换函数时,通常这样写: void Swap(int& a, int&am ...
- C++模板详解
参考:C++ 模板详解(一) 模板:对类型进行参数化的工具:通常有两种形式: 函数模板:仅参数类型不同: 类模板: 仅数据成员和成员函数类型不同. 目的:让程序员编写与类型无关的代码. 注意:模板 ...
- c3p0-config.xml模板详解
c3p0-config.xml模板详解 <c3p0-config> <default-config> <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数.De ...
- C++ 类模板详解(一):概念和基本使用方式
与函数模板类似地(C++函数模板详解(一):概念和特性) ,类也可以被一种或多种类型参数化.例如,容器类就是一个具有这种特性的典型例子,它通常被用于管理某种特定类型的元素.只要使用类模板,我们就可以实 ...
- C++模板详解(三):参数化声明详解
在前两节中(C++模板详解(一).C++模板详解(二)),我们了解了函数模板和类模板的基本概念和使用方法.在这篇博文里,我们主要来详细地阐述一下"模板的参数声明"这个话题,并且也谈 ...
- 【转】 C++模板详解
C++模板 模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数.返回值取得任意类型. 模板是一种对类型进行参数化的工具: 通常有 ...
- Percona监控MySQL模板详解
InnoDB Adaptive Hash Index 显示了"自适应哈希索引"的使用情况,哈希索引只能用来搜索等值的查询. # Hash table size 17700827, ...
- 28.C++- 单例类模板(详解)
单例类 描述 指在整个系统生命期中,一个类最多只能有一个实例(instance)存在,使得该实例的唯一性(实例是指一个对象指针) , 比如:统计在线人数 在单例类里,又分为了懒汉式和饿汉式,它们的区 ...
- C++模板详解——使用篇
假如我们需要取得两个变量中较大的变量,或许,我们可以通过重载的方式实现,如下. int max(int fir,int sec); float max(float fir,float sec); do ...
随机推荐
- spring 框架学习网站
spring 框架学习网站 NO1 http://www.mkyong.com NO2 https://spring.io/docs/reference
- Windows 10 IoT Serials 10 – 如何使用OCR引擎进行文字识别
1. 引言 OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗.亮的模式确定其形状,然后用字符识别方 ...
- web安全Wargame—Natas解题思路(1-26)
前言: Natas是一个教授服务器端Web安全基础知识的 wargame,通过在每一关寻找Web安全漏洞,来获取通往下一关的秘钥,适合新手入门Web安全. 传送门~ 接下来给大家分享一下,1-20题的 ...
- Javascript高级编程学习笔记(35)—— DOM(1)节点
DOM JS由三部分组成 1.BOM 2.DOM 3.ECMAScript ES和BOM在前面的文章已经介绍过了 今天开始JS组成的最后一部分DOM(文档对象模型) 我们知道,JS中的这三个部分实际上 ...
- HC-06蓝牙模块的使用
HC-06蓝牙模块与HC-05的AT指令变化还是挺大的,在模块上电后红灯闪烁表示未连接成功,常亮表示连接成功,期间只要红灯处于闪烁即是进入了AT模式,可发送AT指令,灯常亮使用AT指令无效.下面是常用 ...
- 【面试篇】寒冬求职季之你必须要懂的原生JS(中)
互联网寒冬之际,各大公司都缩减了HC,甚至是采取了“裁员”措施,在这样的大环境之下,想要获得一份更好的工作,必然需要付出更多的努力. 一年前,也许你搞清楚闭包,this,原型链,就能获得认可.但是现在 ...
- WebService学习-第一弹
一:WebService简介(1)简介----百度百科(注意标识的重点) Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言 ...
- JavaScript易错点 -- 数组比较
记得当初初学JavaScript时尝试用“==”或“===”比较两个数组是否相等, var a = [1,2,3] var b = [1,2,3] if(a == b){ //false //do s ...
- Java多线程之一
进程与线程 进程 进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位,比如我们windows电脑上运行的一个程序就是一个进程.在传统进程中进程是资源分配和调度的一个基本单位,在后来引入线 ...
- linux:终端常用命令 + vi命令修改文件及保存 方法
首先介绍一下Ubuntu下各个目录的一般作用: / 这就是根目录,一台电脑有且只有一个根目录,所有的文件都是从这里开始的.举个例子:当你在终端里输入“/home”,你其实是在告诉电脑,先从/(根目录 ...