C++中的类模板

类模块的概念和意义

在C++中有这样一些类:

  • 主要用于存储和组织数据元素
  • 类中数据组织的方式和数据元素的具体类型无关
  • 如:数组类,链表类,Stack类,Queue类等

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

所以C++中的类模板是这样的:

  • 以相同的方式处理不同类型的数据
  • 在类声明前使用template进行标识
  • < typename T >用于说明类中使用的泛指类型 T
  • 语法:
template<typename T>
class Operator
{
public:
    T op(T a, T b);
};

类模板的应用:

  • 只能显示指定具体类型,无法自动推导
  • 使用具体类型 < Type > 定义对象
  • 用法:
Operator <int> op1;
Operator <string> op2;
int i = op1.op(1, 2);
string s = op2.op("D.T.", "Software");

类模板的进一步理解:

  • 声明的泛指类型 T 可以出现在类模板的任意地方
  • 编译器对类模板的处理方式和函数模板相同
    • 从类模板通过具体类型产生不同的类
    • 在声明的地方对类模板代码本身进行编译
    • 在使用的地方对参数替换后的代码进行编译

这里举一个例子:

#include <iostream>
#include <string>

using namespace std;

//重载string类减号类型操作符
string operator-(string& l, string& r)
{
    return "Minus";
}

//定义一个类模板
//在类模板中有4个操作
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;
    }
};

int main()
{
    //使用类模板创建一个对象,类型为int
    Operator<int> op1;
    //对象调用类的成员函数
    cout << op1.add(1, 2) << endl;
    
    //使用类模块创建一个对象,类型为string
    Operator<string> op2;
    //对象调用类的成员函数
    cout << op2.add("D.T.", "Software") << endl;
    cout << op2.minus("D.T", "Software") << endl;
    
    return 0;
}

输出结果为:

3
D.T.Software
Minus

类模板在工程中是怎么使用的呢?

  • 类模块必须在头文件中定义
  • 类模块不能分开实现在不同的文件中
  • 类模块外部定义的成员函数需要加上模板 < > 声明

这里做一个示例:

在头文件 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)
{
    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

调用使用时:

#include <iostream>
#include <string>
#include "Operator.h"

using namespace std;

int main()
{
    //使用类模块创建类对象
    Operator<int> op1;
    
    //类对象使用各个成员函数
    cout << op1.add(1, 2) << endl;
    cout << op1.multiply(4, 5) << endl;
    cout << op1.minus(5, 6) << endl;
    cout << op1.divide(10, 5) << endl;
    
    return 0;
}

输出结果为:

3
20
-1
2

多参数类模块

在类模块中可以定义任意多个不同的类型参数,比如这样:

template 
<typename T1, typename T2>
class Test
{
public:
    void add(T1 a, T2 b);
};

//使用
Test<int, float> t;

类模块的特化

这里再学习一个类模块的知识,就是它可以被 特化

  • 指定类模块的特定实现
  • 部分类型参数必须显示指定
  • 根据类型参数分开实现类模块
  • 语法:
template
<typename T1, typename T2>
class Test
{
};

//特化
template
<typename T>
class Test <T, T>
{
};

类模块可以被 特化 ,当然对于特化还有特化类型:

  • 部分特化 --- 用特定规则约束类型参数
  • 完全特化 --- 完全显示指定类型参数
template
<typename T1, typename T2>
class Test
{
};

//完全特化
template
<  >
class Test < int, int >
{
};

这里举一个例子:

#include <iostream>
#include <string>

using namespace std;

template
< typename T1, typename T2 >
//正常的类模块
class Test
{
public:
    void add(T1 a, T2 b)
    {
        cout << "void add(T1 a, T2 b)" << endl;
        cout << a + b << endl;
    }
};

template
< typename T1, typename T2 >
// 关于指针的特化实现
//部分特化:用指针类型约束类型参数
class Test < T1*, T2* >
{
public:
    void add(T1* a, T2* b)
    {
        cout << "void add(T1* a, T2* b)" << endl;
        cout << *a + *b << endl;
    }
};

template
< typename T >
// 当 Test 类模板的两个类型参数完全相同时,使用这个实现
//部分特化:用参数类型完全相等的规则约束
class Test < T, T >
{
public:
    void add(T a, T b)
    {
        cout << "void add(T a, T b)" << endl;
        cout << a + b << endl;
    }
    void print()
    {
        cout << "class Test < T, T >" << endl;
    }
};

template
<  >
// 当 T1 == void* 并且 T2 == void* 时
//完全特化 完全显示指定类型参数
class Test < void*, void* >
{
public:
    void add(void* a, void* b)
    {
        cout << "void add(void* a, void* b)" << endl;
        cout << "Error to add void* param..." << endl;
    }
};

int main()
{  
    //2个类型不同,调用普通类模块
    Test<int, float> t1;
    //2个类型相同,调用用参数类型完全相等的规则约束的类模块
    Test<long, long> t2;
    //2个类型完全相等,并且符合已经指定参数类型的类模板
    Test<void*, void*> t3;
    
    t1.add(1, 2.5);
    
    t2.add(5, 5);
    t2.print();
    
    t3.add(NULL, NULL);
    //2个参数都为指针,且类型不同
    //调用用指针类型约束类型参数的类模板
    Test<int*, double*> t4;
    int a = 1;
    double b = 0.1;
    
    t4.add(&a, &b);
    
    return 0;
}

输出结果为:

void add(T1 a, T2 b)
3.5
void add(T a, T b)
10
class Test < T, T >
void add(void* a, void* b)
Error to add void* param...
void add(T1* a, T2* b)
1.1

使用类模块特化也要注意一些地方:

  • 特化只是模块的分开实现
    • 本质上是同一个类模块
  • 特化类模板的使用方式是统一的
    • 必须显示指定每一个类型参数

类模块特化的进一步理解

其实有没有发现特化和重定义有点相似,但也有些区别:

  • 重定义
    • 一个类模块和一个新类(或者两个类模块)
    • 使用的时候需要考虑如何选择的问题
  • 特化
    • 以统一的方式使用类模块和特化类
    • 编译器自动优先选择特化类

那既然类模块可以特化,函数模块可不可以特化呢?

  • 函数模板只支持类型参数完全特化
  • 使用方法:
template
<typename T>
//函数模块定义
bool Equal(T a, T b)
{
    return a == b;
}

template
< >
//函数模块完全特化
bool Equal<void *>(void* a, void* b)
{
    return a == b;
}

这里举一个例子:

#include <iostream>
#include <string>

using namespace std;

//普通函数模块
template
< typename T >
bool Equal(T a, T b)
{
    cout << "bool Equal(T a, T b)" << endl;
    
    return a == b;
}

//完全特化后的函数模块
template
< >
bool Equal<double>(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;
    
    cout << "bool Equal<double>(double a, double b)" << endl;
    
    return (-delta < r) && (r < delta);
}

//普通函数
bool Equal(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;
    
    cout << "bool Equal(double a, double b)" << endl;
    
    return (-delta < r) && (r < delta);
}

int main()
{  
    //调用函数
    cout << Equal( 1, 1 ) << endl;
    //调用完全特化后的函数模块
    cout << Equal<>( 0.001, 0.001 ) << endl;
    
    return 0;
}

输出结果为:

bool Equal(T a, T b)
1
bool Equal<double>(double a, double b)
1

这里要注意:

当需要重载函数模块时,优先考虑使用模板特化;当模板特化无法满足需求,再使用函数重载!

小结

  • 泛型编程的思想应用于类的形式就是类模块
  • 类模板以相同的方式处理不同类型的数据
  • 类模块非常适用于编写数据结构相关的代码
  • 类模块在使用时只能显示指定类型
  • 类模块可以定义任意多个不同的类型参数
  • 类模块可以被部分特化和完全特化
  • 特化的本质是模板的分开实现
  • 函数模板只支持完全特化
  • 工程中使用模板特化代替类(函数)重定义
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,676评论 18 139
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,521评论 1 51
  • C++ 模板简介 一、模板 使用模板的目的就是能够让程序员编写与类型无关的代码。 模板是一种对类型进行参数化的工具...
    MinoyJet阅读 2,379评论 0 12
  • 俗话说:人在职场混,哪能不挨刀。 职场潜规则犹如宫斗般精彩,谁都可能中枪,谁都可能背后插你一刀。 遇到爱给你穿小鞋...
    msdone阅读 446评论 0 0
  • 我的梦想:成为一名优秀的幼师! 1职业目标:在毕业后进入早教行业top10的天才宝贝早教公司实习,并争取拿下该公...
    稚童千阳阅读 397评论 0 0