c++中的类模板

2023-11-20

C++的类模板为生成通用的类声明提供了一种更好的方法。模板提供参数化类型,即能够将类型名作为参数传递给接收方来建立类或者函数。

一、定义类模板

#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Pair
{
public:
    T1 key;  //关键字
    T2 value;  //值
    Pair(T1 k,T2 v):key(k),value(v) { };
    bool operator < (const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
//Pair的成员函数 operator <
{ //"小"的意思就是关键字小
    return key < p.key;
}
int main()
{
    Pair<string,int> student("Tom",19); //实例化出一个类 Pair<string,int>
    cout << student.key << " " << student.value;
    return 0;
}

不能将模板成员函数放在独立的实现文件中(以前,C++提供了关键字export,让您能够将模板成员函数放在独立的实现文件中,但是支持的编译器不多,C++11不再使用这样的关键字),由于模板不是函数,不能单独编译,模板必须与特定的模板实例化请求一起使用,为此,最简单的方法是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该头文件。

仅在程序包含模板并不能生成模板类,而必须请求实例化,为此,需要声明一个类型为模板类的对象,方法是使用所需的具体类型替换泛型名。

二、深入探讨模板类

指针栈

可以将内置类型或类对象用作类模板的类型,指针可以嘛?答案是可以,可以创建指针栈,但是如果不对程序做重大修改,将无法很好的工作,编译器可以创建类,但是使用效果就因人而异了。

数组模板示例和非类型参数

模板常用作容器类,这是因为类型参数的概念非常适合于将相同的存储方案用于不同的类型。确实,为容器类提供可重用代码是引入模板的主要动机,所以我们来看看另一个例子,深入探讨模板设计和使用的其他几个方面。具体地说,将探讨一些非类型(或表达式)参数以及如何使用数组来处理继承族。

首先介绍一个允许指定数组大小的简单数组模板。一种方法是在类中使用动态数组和构造函数参数来提供元素数目,最后一个版本的Stack模板采用的就是这种方法。另一种方法是使用模板参数来提供常规数组的大小,C++11新增的模板array就是这样做的。

#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include <iostream>
#include <cstdlib>

template<class T, int n>
class ArrayTP {
private:
    T at[n];
public:
    ArrayTP() {};

    explicit ArrayTP(const T &v);

    virtual T &operator[](int i);

    virtual T operator[](int i) const;
};

template<class T, int n>
ArrayTP<T, n>::ArrayTP(const T &v) {
    for (int i = 0; i < n; i++) {
        at[i] = v;
    }
}

template<class T, int n>
T &ArrayTP<T, n>::operator[](int i) {
    if (i < 0 || i >= n) {
        std::cerr << i << "is out of range\n" << std::endl;
        std::exit(EXIT_FAILURE);
    }
    return at[i];
}

template<class T, int n>
T ArrayTP<T, n>::operator[](int i) const {
    if (i < 0 || i >= n) {
        std::cerr << i << "is out of range\n" << std::endl;
        std::exit(EXIT_FAILURE);
    }
    return at[i];
}

#endif

关键字class(或在这种上下文中等价的关键字typename)指出T为类型参数,int 指出n的类型为int。这种参数(指定特殊的类型而不是用作泛型名)称为非类型(non-type)或表达式(expression)参数。假设有下面的声明:

ArrayTP<double, 10> arrayTp(3.14);

这将创建一个存储double类型对象,编译器使用double替换了T,使用10替换了n。

表达式参数有一些显示,表达式参数可以使整型,枚举,引用或者指针。因此double n是不合法的,但是double *pm是合法的,另外,模板代码不能修改参数的值,也不能使用参数的地址,所以在这个模板中不能使用n++或者&n这类的表达式,另外,实例化模板时,用作表达式参数的值必须是常量表达式。

表达式参数的柱要求但是每种数组大小都将生成自己的模板,也就是说,下面的声明将生成两个独立的类声明:

ArrayTP<double, 10> arrayTp1(3.14);
ArrayTP<double, 12> arrayTp2(3.14);

模板多功能性

可以将用于常规类的技术用于模板类。模板类可用作基类,也可用作组件类,还可用作其他模板的类型参数。例如,可以使用数组模板实现栈模板,也可以使用数组模板来构造数组——数组元素是基于栈模板的栈。

也可以递归的使用模板

ArrayTP<Array<int,5>,10> twodee;

与之等价的常规数组声明如下

int twodee[10][5];

默认参数

木板的另一项新特性是,可以为类型参数提供默认值。

template <class T1, class T2 = int> 
class Topo {
	...
}

三、模板的具体化

类模板与函数模板很相似,因为可以有隐式实例化、显式实例化和显式具体化,它们统称为具体化(specialization)。模板以泛型的方式描述类,而具体化是使用具体的类型生成类声明。

3.1、隐式实例化

到目前为止,本章所有的模板示例使用的都是隐式实例化(implicit instantiation),即它们声明一个或多个对象,指出所需的类型,而编译器使用通用模板提供的处方生成具体的类定义:

ArrayTP <int,100> stuff;

编译器在需要对象之前,不会生成类的隐式实例化:

ArrayTP <double,100> *tp;
tp = new ArrayTP <double,100>;

第二条语句导致编译器生成类定义,并根据该定义创建一个对象。

3.2、显式实例化

当使用关键字template 并指出所需类型来声明类时,编译器将生成类声明的显式实例化( explicit instantiation)。声明必须位于模板定义所在的名称空间中。例如,下面的声明将ArrayTP<string,100>声明为一个类;

template class ArrayTP <int,100>;

在这种情况下,虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板来生成具体化。

3.3、显式具体化

显式具体化(explicit specialization)是特定类型(用于替换模板中的泛型)的定义。有时候,可能需要在为特殊类型实例化时,对模板进行修改,使其行为不同。在这种情况下,可以创建显式具体化。

假设模板使用>运算符来对值进行比较,对于数字这管用,如果T表示一种类,则只要定义了T::operator>()方法,这也管用;但是T如果是由const char *表示的字符串,这将不管用,此时可以采用具体化方案,提供一个显示模板具体化,这将采用为具体类型定义的模板而不是为泛型定义的模板,当具体化模板和通用模板都与实例化请求匹配时,编译器将使用具体化版本。

具体化类模板定义格式

template <> class Classname<specialized-type-name> {...};

早期的编译器可能只能识别早期的格式,这种格式不支持前缀

class Classname<specialized-type-name> {...};

使用新的表示法提供一个专供const char*使用的模板,可以使用类似于下面的代码

template <> class ArrayTp<chonst char*> {
	...
};

3.4、部分具体化

C++还允许部分具体化(partial specialization),即部分限制模板的通用性。例如,部分具体化可以给类型参数之一指定具体的类型

// 通用模板
template <class T1,class T2> class Pair {...}
// 部分具体化
template <class T1> class Pair<T1,int> {...}

关键字template后面的<>声明是没有被具体化的类型参数。因此,上述第二个声明将T2具体化为int,但T1保持不变,注意,如果指定所有的类型,则<>内将为空,这将导致显式具体化。

tempplate <> class Pair<int,int> {...}

如果有多个模板可以选择,编译器将使用具体化程度最高的模板。

3.5、成员模板

模板可用作结构、类、或模板类的成员,要实现完全STL的设计,必须使用这项特性。

#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include <iostream>

using std::cout;
using std::endl;

template<typename T>
class beta {
private:
    template<typename V>
    class hold {
    private:
        V val;
    public:
        hold(V v = 0) : val(v) {}

        void show() const { cout << val << endl; }

        V value() const { return val; }
    };

    hold<T> q;
    hold<int> n;
public:
    beta(T t, int i) : q(t), n(i) {}

    template<typename U>
    U blab(U u, T t) { return (n.value() + q.value()) * u / t; }

    void show() const {
        q.show();
        n.show();
    }
};

#endif


int main() {
    beta<double> guy(3.5,3);
    guy.show();
    cout << "U was set to int" << endl;
    cout << guy.blab(10,2.3) << endl;
    cout << "U was set to double" << endl;
    cout << guy.blab(10.0,2.3) << endl;
    
    // 我们也可以显式的转化
    cout << guy.blab<int>(10.0,2.3) << endl;
}

四、将模板用作参数

您知道,模板可以包含类型参数如typename T和非类型参数 int n,模板还可以本身就是模板的参数,这种参数是模板新增的特性,用于实现STL。

template <template <typename T> class Thing>

模板参数是template < typename T > class Thing,其中template < typename T > class是类型 Thing是参数,这意味着什么呢,假设有下面的声明

Crab<King> legs;

为了使上面的声明被接受,模板参数King必须是一个模板类,其声明与模板参数Thing的声明匹配

template <typename T>
class King {...}

五、模板别名

如果能为类型指定别名,将很方便,在模板设计中尤其如此。可使用typedef 为模板具体化指定别名:

typedef std::array<double,12> arrd;
typedef std::array<int,12> arri;
typedef std::array<std::string,12> arrs;

arrd gallons;
arri days;
arrs months;

C++11新增了意向功能,使用模板提供一系列别名。

template <typename T>
using arrtype = std::array<T,12>;

这将arrtype定义为一个模板别名,可使用它来指定类型,如下所示:

arrtype<double> gallons;
arrtype<int> days;
arrtype<std::string> months;

C++11允许将语法using=用于非模板,用于非模板时这种语法与常规typedef等价

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++中的类模板 的相关文章

随机推荐

  • cocos 基础动作加上简单特效

    使用文理缓存创建精灵 cc Director getInstance getTextureCache addImage WechatIMG3 png localsp cc Sprite createWithTexture cc Direct
  • Error inflating class androidx.constraintlayout.widget.ConstraintLayout

    今天下载了android studio 3 3 1体验体验新版本来着 没想到新建项目直接来了个这个 android view InflateException Binary XML file line 2 Error inflating c
  • 常见的距离算法和相似度(相关系数)计算方法

    摘要 1 常见的距离算法 1 1欧几里得距离 Euclidean Distance 以及欧式距离的标准化 Standardized Euclidean distance 1 2马哈拉诺比斯距离 Mahalanobis Distance 1
  • vue3 ---- 递归组件生成menu菜单 && 路由守卫鉴权

    目录 递归组件 el menu 父组件 子组件 路由 Vue路由守卫实现登录鉴权 全局守卫 路由独享的守卫 组件内的守卫 完整的导航解析流程 菜单权限 按钮权限 对于一些有规律的DOM结构 如果我们再一遍遍的编写同样的代码 显然代码是比较繁
  • IDEA切换分支导致项目异常, 部分类爆红问题解决

    关于idea切换分支导致项目异常爆红的方式解决两种办法 1 maven 并没有及时刷新 所以 当我们第一时间出现这个问题的时候 首选是刷新maven 如图所示 2 如果刷新mavne 还是没有解决idea 项目爆红的情况的话 那我们就需要考
  • 计算机不能创建用户,Windows10系统无法创建新用户该怎么办?

    由于工作需要 需要对同一台计算机创建多个用户帐户 Windows7操作系统创建新用户的方法很简单 简单几步就能够轻松完成创建 参照Windows7操作系统创建新用户的步骤 发现并不适用于Windows10操作系统 系统会提示需要登录Micr
  • CocosCreator波浪Shader

    waveEffect effect Copyright c 2017 2020 Xiamen Yaji Software Co Ltd CCEffect techniques passes vert sprite vs vert frag
  • Serverless 的前世今生

    作者 阿里云用户组 从云计算到 Serverless 架构 大家好 我是阿里云 Serverless 产品经理刘宇 很高兴可以和大家一起探索 Serverless 架构的前世今生 从云计算到云原生再到 Serverless 架构 技术飞速发
  • 【推荐算法】双塔模型代码(tensorflow)

    推荐算法 双塔模型介绍 MachineCYL的博客 CSDN博客 上文介绍了双塔模型的原理和结构 这篇介绍一下双塔模型的代码实现 我使用的是tensorflow来实现双塔模型和模型训练 一 前期准备 tensorflow使用的版本是2 0
  • 正激拓扑的复位电路

    正激拓扑的复位电路 1 杂谈 2 原理简介 3 分类 3 1 有源钳位 3 2 绕组复位 3 3 RCD复位 3 4 谐振复位 1 杂谈 我发现我有个最大的缺点是不会讲话 每次跟不熟悉的人讲话或者汇报时 就毫无逻辑 紧张的要死 被讨厌的勇气
  • Navicat设置id自增,并随时设置自增的起始值

    一 问题背景 有时候数据里面的表的id为 1 4 13 15等等 如下图 怎么重新设置自增的起始值 使它变成每个增加1 而不是散列的数 如下效果 二 解决方法 选中要修改的表 点击设计表 如下 然后点击 选项 在自动递增那里改为1即可 点击
  • 2022全国职业技能大赛-网络安全赛题解析总结④(超详细)

    2022全国职业技能大赛 网络安全赛题解析总结 自己得思路 模块A 基础设施设置与安全加固 20分 模块B 网络安全事件响应 数字取证调查和应用安全 40分 模块C CTF夺旗 攻击 20分 模块D CTF夺旗 防御 20分 有什么不懂得可
  • MATLAB实现PSO-SVM多输入单输出回归预测(粒子群算法优化支持向量机)

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 更多Matlab仿真内容点击 智能优化算法 神经网络预测 雷达通信 无线传感器 电力系统 信号
  • 循环 for while do..while 以及break和continue

    循环 for 双重for while dowhile continue break 一 for循环 被循环的执行语句为循环体 是否继续执行取决于终止条件语句 所以 循环语句由 循环体和循环的终止条件组成的语句 语句结构 for 初始化变量
  • 桌面怎么设置 计算机 网络连接,电脑桌面的本地连接ip地址可以设置吗_本地连接ip地址设置方法 - 驱动管家...

    1 首先在Win7桌面上找到 网络 入口 如下图 进入Win7网络 2 进入网络之后我们再点击顶部的 网络共享中心 如下图 进入Win7网络共享中心 3 进入Win7网络共享中心之后 我们再点击左侧的 更改网络适配器 如下图 选择更改网络适
  • 经典,一文讲透ESD原理和设计

    一直想给大家讲讲ESD的理论 很经典 但是由于理论性太强 任何理论都是一环套一环的 如果你不会画鸡蛋 注定了你就不会画大卫 先来谈静电放电 ESD Electrostatic Discharge 是什么 这应该是造成所有电子元器件或集成电路
  • Ubuntu20.04通过rsync和inotify实现定时备份与实时备份

    通过rsync和inotify实现定时备份与实时备份 为了避免主服务单点故障 可以将数据备份到远程备份机器 可以使用rsync工具同步Jenkins home到远程 可以利用rsync工具的 exclude from FILE 功能 定制一
  • KVM热迁移

    KVM热迁移 介绍 KVM热迁移的前提是拥有共享存储 以下通过NFS实现KVM热迁移 迁移过程 将一处于运行状态的KVM虚拟机从节点kvm 01迁移到kvm 02后继续运行 准备 主机准备 hostname IP地址 系统 配置 kvm 0
  • Docker 国内镜像地址

    http f1361db2 m daocloud io http hub mirror c 163 com https docker mirrors ustc edu cn
  • c++中的类模板

    C 的类模板为生成通用的类声明提供了一种更好的方法 模板提供参数化类型 即能够将类型名作为参数传递给接收方来建立类或者函数 一 定义类模板 include