类模板(四十八)

我们上节博客介绍了泛型编程思想,那么在 C++ 中是否可以将泛型的思想应用于类呢?答案肯定是显而易见的,在 C++ 中的标准库中,就是一些通用的类模板。我们先来看看类模板有哪些特性,它主要是用于存储和组织数据元素,类中数据组织的方式和数据元素的具体类型无关,如:数组类、链表类、Stack 类等。C++ 中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。

在 C++ 中的类模板是以相同的方式处理不同的类型,并且在类声明前使用 template 进行标识。< typename T > 用于说明类中使用的泛指类型 T。类模板只能显示指定具体类型,无法自动推导;使用具体类型(Type)定义对象。声明的泛指类型 T 可以出现在类模板的任意地方;编译器对类模板的处理方式和函数模板相同:即 a> 从类模板通过具体类型产生不同的类;b> 在声明的地方对类模板代码本身进行编译;c> 在使用的地方对参数替换后的代码进行编译。

下来我们还是以代码为例来进行分析

#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;
    }
};

int main()
{
    Operator<int> op1;
    
    cout << "op1.add(1, 2) = " << op1.add(1, 2) << endl;
    
    Operator<string> op2;
    
    cout << op2.add("D.T.", "Software") << endl;
    //cout << op2.minus("D.T.", "Software") << endl;
    
    return 0;
}

我们来试试看看能不能实现整数和字符串的相加操作呢?

我们看到已经实现了,那么我们再来试试字符串的减法呢?

我们看到字符串是不支持减法的,因为编译器不知道该怎么进行操作。下来我们写一个减法操作符的重载函数,看看还会出错嘛,如下

string operator- (string& l, string& r)
{
    return "Minus";
}

编译结果如下

我们看到编译没报错,也就是说,编译器在编译到字符串的减法操作时,它发现不支持,但是为了显示它的强大没先报错,先去查找有没有实现好的重载函数。结果找到了,便去调用这个函数了。

类模板在工程中的应用遵从以下几条规则:1、类模板必须在头文件中定义;2、类模板不能分开实现在不同的文件中;3、类模板外部定义的成员函数需要加上模板<>声明。下来我们就以工程中的标准再来实现下上面的类模板

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

test.cpp 源码

#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;
}

我们编译后结果如下

我们看到运行已经得到正确的结果了。那么我们接下来想想类模板能否支持泛型编程中的那种定义任意多个不同类型的参数吗?显然是肯定支持了。如下

类模板可以是可以被特化的:a> 指定类模板的特定实现;b> 部分类型参数必须显示指定;c> 根据类型参数分开实现类模板。如下所示

类模板的特化类型分为两种:部分特化和完全特化。部分特化是指用特定规则来约束类型参数,而完全特化则是指完全显示指定类型参数。如下

下来我们还是以代码为例来进行分析说明

#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*, T2*)" << endl;
        cout << *a + *b << endl;
    }
};

template
< typename T >
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
<  >
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()
{
    Test<int, float> t1;
    Test<long, long> t2;
    Test<void*, void*> t3;
    
    t1.add(1, 2.5);
    
    cout << endl;
    
    t2.add(5, 5);
    t2.print();
    
    cout << endl;
    
    t3.add(NULL, NULL);
    
    cout << endl;
    
    Test<int*, double*> t4;
    int a = 1;
    double b = 0.1;
    
    t4.add(&a, &b);
    
    return 0;
}

我们先是定义了一个模板类,接着定义了它的部分特化(参数类型为 void*以及参数类型是相同的),最后一个是完全特化。我们来看看编译结果

我们看到编译是通过的,说明编译器是支持这样的写法的,也正常运行结束了。那么类模板特化还要注意几个事项:1、特化只是模板的分开实现,其本质上还是用一个类模板;2、特化类模板的使用方式是统一的,必须显示指定每一个类型参数。那么类模板特化与重定义有区别吗?函数模板可以特化吗?重定义和特化是不同的,进行重定义时是:一个类模板和一个新类(或者两个类模板),使用的时候需要考虑如何选择的问题。函数模板是可以特化的,以统一的方式使用类模板和特化类,编译器自动优先选择特化类。函数模板只支持类型参数完全特化,如下

下来我们还是以代码为例来进行分析

#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;
    }
};

class Test_void
{
public:
    void add(void* a, void* b)
    {
        cout << "void add(void* a, void* b)" << endl;
        cout << "Error to add void* param..." << endl;
    }
};

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.0000000000001;
    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;
}

我们看到重定义则是类 Test_void 那样的,我们在底下定义了 Equal 函数的特化模板,下来编译看看结果

我们看到编译器确实支持函数特化的写法,并且完美运行。下来我们将全局函数 Equal 的注释去掉,再来看看编译结果

我们看到调用的是全局函数,如果我们非要在这调用我们的函数特化模板该怎样做呢?我们只需在 main 函数中的最后一个 Equal 函数加上 <>,改为 cout << Equal<>( 0.001, 0.001 ) << endl;再来看看编译结果

我们看到已经已经成功的调用函数模板了。在工程中的建议是:当需要重载函数模板时,优先考虑使用模板特化;当模板特化无法满足需求时,再来使用函数重载!通过对类模板的学习,总结如下:1、泛型编程的思想是可以应用于类的;2、类模板以相同的方式处理不同类型的数据;3、类模板非常适用于编写数据结构相关的代码,它在使用时只能显示指定类型;4、类模板可以定义任意多个不同的类型参数;5、类模板可以被部分特化和完全特化,其特化的本质是模板的分开实现;6、函数模板只支持完全特化,工程中使用模板特化代替类(函数)重定义。

欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083。

原文地址:http://blog.51cto.com/12810168/2123318

时间: 2025-01-08 03:12:50

类模板(四十八)的相关文章

QT开发(四十八)——数据库SQL接口层

QT开发(四十八)--数据库SQL接口层 SQL接口层提供了对数据库的访问,主要类包括Qt SQL模块中的QSqlDatabase.QSqlQuery.QSqlError.QSqlField.QSqlIndex和QSqlRecord.QSqlDatabase类用于创建数据库连接,QSqlQuery用于使用SQL语句实现与数据库交互. 一.QSqlDatabase 1.QSqlDatabase简介 QSqlDatabase类提供了通过连接访问数据库的接口,QSqlDatabase对象本身代表一个连

JavaWeb学习总结(四十八)——模拟Servlet3.0使用注解的方式配置Servlet

JavaWeb学习总结(四十八)——模拟Servlet3.0使用注解的方式配置Servlet 一.Servlet的传统配置方式 在JavaWeb开发中, 每次编写一个Servlet都需要在web.xml文件中进行配置,如下所示: 1 <servlet> 2 <servlet-name>ActionServlet</servlet-name> 3 <servlet-class>me.gacl.web.controller.ActionServlet</s

实验四十八微软应用程序虚拟化之三APP-V 5.1 Client部署和通过组策略自定义配置

实验四十八微软应用程序虚拟化之三APP-V 5.1Client部署和通过组策略自定义配置 APP-V  Client分为Application Virtualization Desktop Client和 Application Virtualization Client for Remote Desktop Services,两者都为虚拟化应用程序提供并管理虚拟环境,管理到缓存的程序包传输.发布刷新.传输,以及与 Application Virtualization Server的所有交互.

你的一天也可以是四十八小时

只要你愿意,你的一天将是四十八小时,而不是二十四小时. 时间就是上帝给你的资本.命运之神是公平的,他给每个人的时间都是公平的,他给每个人的时间都不多不少:但成功女神却是挑剔的,她只让那些能把24小时变成48小时的人接近她.下面就是她的助手时间使者透露出来的成功秘笈: 直奔主题 聪明人要远离琐碎,保持焦点.由于我们一次只能踏上一条船,“船”的选择便显得格外重要.为此,经济学家告诉我们,要保持焦点:一次只做一件事情,一个时期只有一个重点.西屋电脑公司总经理迪席勒办公室门上的标语是:“不要带问题给我,

产品定位四十八招(12)定位盈利专家吴玉龙

第四十招:基于"专用"定位策略<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 广告语"就像刚刚步出美发厅"定位"美发厅的选择"牌是美发厅专用的一种洗发香波. 第四十一招:"一次性使用"定位策略 1987年,柯达公司推出一次性相机获得了巨大的成功.1993年,仅在美国就销售了930万个一次性相

ActionScript3游戏中的图像编程(连载四十八)

总目录:http://blog.csdn.net/iloveas2014/article/details/38304477 3.1.4 简单滤镜中的渐变原理探讨 我们再简单的看一下滤镜里的渐变条. 为了尽可能地减少复杂的文字轮廓所造成的干扰,我再次使用方块进行试验. 我画了一个紫色的方块(啥颜色其实都无所谓了),然后添加渐变发光滤镜,把模糊和距离值都调大,以便看清楚渐变的轮廓,然后给渐变条随意地加上几种颜色(图 3.30) 图 3.30 模糊范围较大的渐变发光 可以看到,渐变条上的色彩从右往左地

【科普】江户四十八手图文最详细解释

完整预览图: 四十八手解説 [日本語]:http://hp.vector.co.jp/authors/VA044354/japaneseindetail.html 较难招式须注意安全: 虽然招式号称48招,不过就连推出教战手册的旅馆业者也认为,一般人试个三.五招其实就差不多了: 而且其中有些动作还属于[超高难度]级 比方说25招[搥衣棒],女方抱住两腿膝盖后侧,下半身直直往头顶举,男方则以相反方向由上向下顶. 还有27招[推车],男方站着将女方双脚抬起来,女方用双手撑在床面,身体成四十五度悬在空

JAVA学习笔记(四十八)- 适配器类 Adapter

适配器类 Adapter,对接口中的抽象方法进行空实现 import java.awt.Button; import java.awt.Frame; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEve

第四十八课 zabbix工作原理、安装、配置入门

监控系统基础及zabbix介绍 zabbix工作原理及安装配置 zabbix配置入门 zabbix配置入门 一.监控系统基础及zabbix介绍 著名的监控工具 zabbix zennos opennms cacti nagios. cacti 收集数据.展示图表 nagios 关注状态 报警机制强 zabbix 强大的监控工具能完成数据采集.存储.展示.报警功能. zabbix 有专用的agent的监控工具,他是一个分布式的监控系统. 二.zabbix的安装(zabbix-2.4为例) 1.rp