详解C++编程中类模板的相关使用知识

有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类:

class Compare_int
{
public :
  Compare(int a,int b)
  {
   x=a;
   y=b;
  }
  int max( )
  {
   return (x>y)?x:y;
  }
  int min( )
  {
   return (x<y)?x:y;
  }
private :
  int x,y;
};

其作用是对两个整数作比较,可以通过调用成员函数max和min得到两个整数中的大者和小者。

如果想对两个浮点数(float型)作比较,需要另外声明一个类:

class Compare_float
{
public :
  Compare(float a,float b)
  {
   x=a;y=b;
  }
  float max( )
  {
   return (x>y)?x:y;
  }
  float min( )
  {
   return (x<y)?x:y;
  }
private :
  float x,y;
}

显然这基本上是重复性的工作,应该有办法减少重复的工作。

C++在发展的后期增加了模板(template )的功能,提供了解决这类问题的途径。可以声明一个通用的类模板,它可以有一个或多个虚拟的类型参数,如对以上两个类可以综合写出以下的类模板:

template <class numtype> //声明一个模板,虚拟类型名为numtype
class Compare //类模板名为Compare
{
public :
  Compare(numtype a,numtype b)
  {
   x=a;y=b;
  }
  numtype max( )
  {
   return (x>y)?x:y;
  }
  numtype min( )
  {
   return (x<y)?x:y;
  }
private :
  numtype x,y;
};

请将此类模板和前面第一个Compare_int类作一比较,可以看到有两处不同。

1) 声明类模板时要增加一行

  template <class 类型参数名>

template意思是“模板”,是声明类模板时必须写的关键字。在template后面的尖括号内的内容为模板的参数表列,关键字class表示其后面的是类型参数。在本例中numtype就是一个类型参数名。这个名宇是可以任意取的,只要是合法的标识符即可。这里取numtype只是表示“数据类型”的意思而已。此时,mimtype并不是一个已存在的实际类型名,它只是一个虚拟类型参数名。在以后将被一个实际的类型名取代。

2) 原有的类型名int换成虚拟类型参数名numtype。
在建立类对象时,如果将实际类型指定为int型,编译系统就会用int取代所有的numtype,如果指定为float型,就用float取代所有的numtype。这样就能实现“一类多用”。

由于类模板包含类型参数,因此又称为参数化的类。如果说类是对象的抽象,对象是类的实例,则类模板是类的抽象,类是类模板的实例。利用类模板可以建立含各种数据类型的类。

那么,在声明了一个类模板后,怎样使用它呢?怎样使它变成一个实际的类?

先回顾一下用类来定义对象的方法:

  Compare_int cmp1(4,7); // Compare_int是已声明的类

其作用是建立一个Compare_int类的对象,并将实参4和7分别赋给形参a和b,作为进 行比较的两个整数。

用类模板定义对象的方法与此相似,但是不能直接写成

  Compare cmp(4,7); // Compare是类模板名

Compare是类模板名,而不是一个具体的类,类模板体中的类型numtype并不是一个实际的类型,只是一个虚拟的类型,无法用它去定义对象。必须用实际类型名去取代虚拟的类型,具体的做法是:

  Compare <int> cmp(4,7);

即在类模板名之后在尖括号内指定实际的类型名,在进行编译时,编译系统就用int取代类模板中的类型参数numtype,这样就把类模板具体化了,或者说实例化了。这时Compare<int>就相当于前面介绍的Compare_int类。

[例] 声明一个类模板,利用它分别实现两个整数、浮点数和字符的比较,求出大数和小数。

#include <iostream>
using namespace std;
template <class numtype>
//定义类模板
class Compare
{
  public :
  Compare(numtype a,numtype b)
  {x=a;y=b;}
  numtype max( )
  {return (x>y)?x:y;}
  numtype min( )
  {return (x<y)?x:y;}
  private :
  numtype x,y;
};
int main( )
{
  Compare<int > cmp1(3,7); //定义对象cmp1,用于两个整数的比较
  cout<<cmp1.max( )<<" is the Maximum of two integer numbers."<<endl;
  cout<<cmp1.min( )<<" is the Minimum of two integer numbers."<<endl<<endl;
  Compare<float > cmp2(45.78,93.6); //定义对象cmp2,用于两个浮点数的比较
  cout<<cmp2.max( )<<" is the Maximum of two float numbers."<<endl;
  cout<<cmp2.min( )<<" is the Minimum of two float numbers."<<endl<<endl;
  Compare<char> cmp3(′a′,′A′); //定义对象cmp3,用于两个字符的比较
  cout<<cmp3.max( )<<" is the Maximum of two characters."<<endl;
  cout<<cmp3.min( )<<" is the Minimum of two characters."<<endl;
  return 0;
}

运行结果如下:

7 is the Maximum of two integers.
3 is the Minimum of two integers.

93.6 is the Maximum of two float numbers.
45.78 is the Minimum of two float numbers.

a is the Maximum of two characters.
A is the Minimum of two characters.

还有一个问题要说明: 上面列出的类模板中的成员函数是在类模板内定义的。如果改为在类模板外定义,不能用一般定义类成员函数的形式:

 numtype Compare::max( ) {…} //不能这样定义类模板中的成员函数

而应当写成类模板的形式:

  template <class numtype>
  numtype Compare<numtype>::max( )
  {
    return (x>y)?x:y;
  }

上面第一行表示是类模板,第二行左端的numtype是虚拟类型名,后面的Compare <numtype>是一个整体,是带参的类。表示所定义的max函数是在类Compare <numtype>的作用域内的。在定义对象时,用户当然要指定实际的类型(如int),进行编译时就会将类模板中的虚拟类型名numtype全部用实际的类型代替。这样Compare <numtype >就相当于一个实际的类。大家可以将例子改写为在类模板外定义各成员 函数。

归纳以上的介绍,可以这样声明和使用类模板:
1) 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。

2) 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype)。

3) 在类声明前面加入一行,格式为:

template <class 虚拟类型参数>

如:

  template <class numtype> //注意本行末尾无分号
  class Compare
  {…}; //类体

4) 用类模板定义对象时用以下形式:

  类模板名<实际类型名> 对象名;
  类模板名<实际类型名> 对象名(实参表列);

如:

  Compare<int> cmp;
  Compare<int> cmp(3,7);

5) 如果在类模板外定义成员函数,应写成类模板形式:

  template <class 虚拟类型参数>
  函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}

关于类模板的几点说明:
1) 类模板的类型参数可以有一个或多个,每个类型前面都必须加class,如:

  template <class T1,class T2>
  class someclass
  {…};

在定义对象时分别代入实际的类型名,如:

  someclass<int,double> obj;

2) 和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。

3) 模板可以有层次,一个类模板可以作为基类,派生出派生模板类。有关这方面的知识实际应用较少,本教程暂不作介绍,感兴趣的同学可以自行学习。

(0)

相关推荐

  • C++中的类模板详解及示例

    C++中的函数模板 对于类的声明来说,也有同样的问题.有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类: 复制代码 代码如下: class Compare_int{ public:  Compare(int a,int b)  {   x=a;   y=b;  }   int max()  {   return (x>y)?x:y;  }  int min()  {   return (x<y)?x:y;  } private:  int x,y;}; 其作用是

  • C++函数模板与类模板实例解析

    本文针对C++函数模板与类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与类模板的理解.具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思.泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库). 模板(template)是泛型编程的基础,一个模板就是一个创建类或函数的蓝图或公式.例如,当使用一个vector这样的泛型类型或者find这样的泛型函数

  • C++类模板与模板类深入详解

    1.在c++的Template中很多地方都用到了typename与class这两个关键字,而且有时候二者可以替换,那么是不是这两个关键字完全一样呢? 事实上class用于定义类,在模板引入c++后,最初定义模板的方法为:template<class T>,这里class关键字表明T是一个类型,后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字,它的作用同class一样表明后面的符号为一个类型,这样在定义模板的时候就可以使用下面的方式了:      t

  • 解读C++编程中类模板的三种特化

    1.类模板显式特化 为了进行特化,首先需要一个通用的版本,称主模板.主模板使用了标准库堆算法.  堆 是一种线性化的树形结构,将一个值压入一个堆中, 实际上等于将该值插入到一个树形结构中;将一个值从堆中取出就等于移除并返回堆中最大值.但在处理字符的指针时会碰钉子.堆将按照指针的值进行组织. 我们可以提供一个显式特化版本解决此问题(例1)如果希望除了一个针对const char*的Heap外,还希望提供一个针对char *的Heap;(例2) //主模板 template <typename T>

  • 详解C++编程中类模板的相关使用知识

    有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类: class Compare_int { public : Compare(int a,int b) { x=a; y=b; } int max( ) { return (x>y)?x:y; } int min( ) { return (x<y)?x:y; } private : int x,y; }; 其作用是对两个整数作比较,可以通过调用成员函数max和min得到两个整数中的大者和小者. 如果想对两个浮点数(

  • 详解C++编程中类的成员变量和成员函数的相关知识

    C++类的成员变量和成员函数 类是一种数据类型,它类似于普通的数据类型,但是又有别于普通的数据类型.类这种数据类型是一个包含成员变量和成员函数的一个集合. 类的成员变量和普通变量一样,也有数据类型和名称,占用固定长度的内存空间.但是,在定义类的时候不能对成员变量赋值,因为类只是一种数据类型,本身不占用内存空间,而变量的值则需要内存来存储. 类的成员函数也和普通函数一样,都有返回值和参数列表,它与一般函数的区别是:成员函数是一个类的成员,出现在类体中,它的作用范围由类来决定:而普通函数是独立的,作

  • 详解C++编程中类的声明和对象成员的引用

    C++类的声明和对象的创建 类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量:创建对象的过程也叫类的实例化.每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数. 与结构体一样,类只是一种复杂数据类型的声明,不占用内存空间.而对象是类这种数据类型的一个变量,占用内存空间. 类的声明 类是用户自定义的类型,如果程序中要用到类,必须先进行声明,或者使用已存在的类(别人写好的类.标准库中的类等),C++语法本身并不提供现成的类的名称.结构和内容. 一个简

  • 详解C++编程中的变量相关知识

    在程序运行期间其值可以改变的量称为变量.一个变量应该有一个名字,并在内存中占据一定的存储单元,在该存储单元中存放变量的值.请注意区分变量名和变量值这两个不同的概念,见图 变量名规则 先介绍标识符的概念.和其他高级语言一样,用来标识变量.符号常量.函数.数组.类型等实体名字的有效字符序列称为标识符(identifier).简单地说,标识符就是一个名字.变量名是标识符的一种,变量的名字必须遵循标识符的命名规则. C++规定标识符只能由字母.数字和下划线3种字符组成,且第一个字符必须为字母或下划线.下

  • 详解C++中函数模板的定义与使用

    目录 1. 前言 2. 初识函数模板 2.1 语法 2.2 实例化 2.3 实参推导 3. 重载函数模板 1. 前言 什么是函数模板? 理解什么是函数模板,须先搞清楚为什么需要函数模板. 如果现在有一个需求,要求编写一个求 2 个数字中最小数字的函数,这 2 个数字可以是 int类型,可以是 float 类型,可以是所有可以进行比较的数据类型…… 常规编写方案:针对不同的数据类型编写不同的函数. #include <iostream> using namespace std; //针对 int

  • 详解C#编程获取资源文件中图片的方法

    详解C#编程获取资源文件中图片的方法 本文主要介绍C#编程获取资源文件中图片的方法,涉及C#针对项目中资源文件操作的相关技巧,以供借鉴参考.具体内容如下: 例子: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Drawing; namespace CL { public class RES { /

  • 详解JAVA设计模式之模板模式

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板.它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行.这种类型的设计模式属于行为型模式. 介绍 意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 主要解决:一些方法通用,却在每一个子类都重新写了这一方法. 何时使用:有一些通用的方法. 如何解决:将这些通用算法抽象出来. 关键代码:在抽象类实现,其他步骤在子

  • 详解C++11 变参模板

    1.概述 变参模板(variadic template)是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数.任意类型的参数.相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进.然而由于可变模版参数比较抽象,使用起来需要一定的技巧,掌握也存在一定的难度. 2.可变模版参数的展开 可变模板参数和普通模板参数的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号"-"

  • 详解使用Mybatis-plus + velocity模板生成自定义的代码

    pom.xml文件的配置 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId&

  • 详解C++11中模板的优化问题

    1. 模板的右尖括号 在泛型编程中,模板实例化有一个非常繁琐的地方,那就是连续的两个右尖括号(>>)会被编译器解析成右移操作符,而不是模板参数表的结束.我们先来看一段关于容器遍历的代码,在创建的类模板 Base 中提供了遍历容器的操作函数 traversal(): // test.cpp #include <iostream> #include <vector> using namespace std; template <typename T> class

随机推荐