C++开发基础——类模板

一,基础定义

类模板是用来生成类的蓝图,是一种创建类的方式,同一套类模板可以生成很多种不同的类。

编译器基于类模板生成的每个类被称为类模板的实例。

第一次使用模板类型声明变量时,会创建类模板的一个实例, 以后定义同类型的变量时,会使用已经创建的第一个实例。

类模板有许多应用,最常见的应用是定义容器类。

类模板和类一样,可以有友元,其友元可以是类,函数或者其他模板。

如果一个派生类继承自该类模板,那么这个派生类也必须是模板。

类模板的代码样式:

template <parameter list>
class ClassName
{
    //class definition ...
}

类型模板参数 & 非类型模板参数图示:

代码样例:用类模板实现的Array<T>

template <typename T>
class Array
{
private:
    T* elements;
    size_t size;
public:
    explicit Array<T>(size_t arraySize);      //构造函数
    Array<T>(const Array<T>& array);          //拷贝构造函数
    ~Array<T>();                              //析构函数
    T& operator[](size_t index);              //下标运算符
    Array<T>& operator=(const Array<T>& rhs); //赋值运算符
    size_t getSize() const {return size;}
};

在类模板的内部,可以直接使用类模板名称,不需要显式地带模板参数,因此,在类模板的内部,Array和Array<T>等价。

以上代码可以简化为:

template <typename T>
class Array
{
    private:
        T* elements;
        size_t size;
    public:
        explicit Array(size_t arraySize);
        Array(const Array& array);
        ~Array();
        T& operator[](size_t index);
        Array& operator=(const Array& rhs);
        size_t getSize() const {return size; }
};

类模板参数指定默认值的方式和函数参数一样。

默认值在类模板的声明中指定即可,不需要在成员函数模板中指定默认值。

定义类模板的时候也可以这样写: 

template <typename T=string>
class Array
{
    //code
}

使用默认值来实例化类模板,可以这样写:

Array<> myArray;

二,类模板的成员函数

在类模板的模板体中定义的成员函数,与普通的类一样,成员函数可以看作是所有模板实例的内联函数。

但是在模板体的外部定义的成员函数,语法与普通的类不同,需要将成员函数定义为函数模板。

由于成员函数的函数模板与它们的类模板绑定在一起,所以函数模板使用的参数列表必须与类模板的参数列表完全相同。

1.构造函数模板:

template <typename T>
Array<T>::Array(size_t arraySize):
elements{new T[arraySize]}, size{arraySize}
{

}

2.拷贝构造函数模板:

假定赋值运算符可以用于T类型的变量。

template <typename T>
Array<T>::Array(const Array& array): Array{array.size}
{
    for (size_t i {}; i < size; ++i)
    {
        elements[i] = array.elements[i];
    }
}

3.析构函数模板:

释放给数组分配的堆内存。

template <typename T>
Array<T>::~Array()
{
    delete[] elements;
}

4.下标运算符模板:

template <typename T>
T& Array<T>::operator[](size_t index)
{
    if (index >= size)
    {
        throw std::out_of_range {"Index too large: " + std::to_string(index)};
    }
    return elements[index];
}

5.赋值运算符模板:

template <typename T>
Array<T>& Array<T>::operator=(const Array& rhs)
{
    if (&rhs != this)
    {
        delete[] elements;
        size = rhs.size;
        elements = new T[size];  //may throw std::bad_alloc
        for(size_t i {}; i < size; ++i)
        {
            elements[i] = rhs.elements[i];
        }
    }
    return *this;
}

由类模板创建模板实例时,并不会把所有的成员函数的函数模板都拿去生成模板实例,只有被代码用到的成员函数才会被生成模板实例,例如,由类模板生成某个类时,这个类只进行了创建对象的操作,只有构造函数和析构函数的函数模板会生成模板实例,其他暂时没用到的函数模板,比如拷贝构造函数模板,则不会生成模板实例。简单讲就是,当实例化一个类模板时,它的成员函数对应的函数模板只有在使用时才会被实例化。

声明指向对象的指针并不会创建类模板的实例:

Array<std::string>* obj_ptr;  
//声明了一个指针,不会创建类模板的实例

Array<std::string*> str_obj {10};
//定义了一个对象,会创建类模板的实例,同时还会生成构造函数的函数模板实例

三,非类型模板参数

非类型参数是指模板定义中,带有指定类型的参数。

非类型参数的主要用途是指定容器的大小和上下限。

代码样例如下:

template <typename T, size_t size> //size: 非类型参数
class ClassName

{
    //code
};

注意:类型参数T必须放在非类型参数size的前面。

非类型模板参数还可以在定义的时候给一个初始值,例如:

template <typename T, size_t size = 10>
class ClassName
{
    //code
};

非类型参数支持的数据类型:

整数类型(例如size_t、long)
枚举类型
对象的指针or引用类型
函数的指针or引用类型

非类型参数不支持浮点类型或类类型。

从C++17开始,也可以指定auto,auto& 和 auto* 等作为非类型参数,编译器会自动推导出类型。

代码样例:

a.带有非类型参数的Array类模板:

template <typename T, int startIndex>
class Array

{
private:
    T* elements;
    size_t size;
public:
    explicit Array(size_t arraySize);
    Array(const Array& array);
    ~Array();
    T& operator[](int index);
    Array& operator=(const Array& rhs);
    size_t getSize() const { return size; }
}

b.带有非类型参数的成员函数模板

1.构造函数模板:

template <typename T, int startIndex>
Array<T, startIndex>::Array(size_t arraySize):
elements{new T[arraySize]}, size{arraySize}
{

}

2.拷贝构造函数模板:

template <typename T, int startIndex>
Array<T, startIndex>::Array(const Array& array): Array{array.size}
{
    for (size_t i {}; i < size; ++i)
    {
        elements[i] = array.elements[i];
    }
}

3.析构函数模板:

template <typename T, int startIndex>
Array<T, startIndex>::~Array()
{
    delete[] elements;
}

4.下标运算符模板:

template <typename T, int startIndex>
T& Array<T, startIndex>::operator[](int index)
{
    return const_cast<T&>(std::as_const(*this)[index]);
}

5.赋值运算符模板:

template <typename T, int startIndex>
Array<T, startIndex>& Array<T, startIndex>::operator=(const Array& rhs)
{
    if (&rhs != this)
    {
        delete[] elements;
        size = rhs.size;
        elements = new T[size];  //may throw std::bad_alloc
        for(size_t i {}; i < size; ++i)
        {
            elements[i] = rhs.elements[i];
        }
    }
    return *this;
}

非类型模板参数在使用时需要注意,给非类型参数传不同的实参,将生成不同的模板实例。

代码样例:

以下代码将创建两个不同的模板实例

Array<double, 0> obj1{10};
Array<double, 1> obj2{10};

四,类模板的特例

和函数模板一样,类模板也有特例,被称为类模板的具体化。

当有些模板参数只适用于特定的数据类型,比如可以使用string类型实例化模板,但使用char*类型实例化模板时会报错,此时需要定义类模板的特例。

代码样例:

template <>
class Array<const char*>
{
};

类模板特例的定义必须在原始类模板的定义之后。

五,参考阅读

《C++17入门经典》

《C++ Primer》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/451941.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3D Gaussian Splatting for Real-Time Radiance Field Rendering(慢慢啃,还是挺复杂的)

三个关键要素 从相机配准的过程中得到的稀疏点云开始&#xff0c;使用3D Gaussian表示场景; 3D Gaussian: 是连续体积辐射场能够防止不必要的空空间优化。对 3D Gaussion进行交叉优化和密度控制: 优化各向异性血方差对场景精确表示。使用快速可视感知渲染算法来进行快速的训练…

最好用的流程编辑器bpmn-js系列之基本使用

BPMN&#xff08;Business Process Modeling Notation&#xff09;是由业务流程管理倡议组织BPMI&#xff08;The Business Process Management Initiative&#xff09;开发的一套标准的业务流程建模符号规范。其目的是为用户提供一套容易理解的标准符号&#xff0c;这些符号作…

Java代码审计工程师直播第六期

本期直播课程将深入探讨Java代码审计的关键概念和技术。涵盖课题包括安全漏洞分析、代码审查方法、常见漏洞案例分析等。学员将通过实例掌握代码审计实战技能&#xff0c;提升对Java应用程序安全的认知和技能水平。 课程大小&#xff1a;6.1G 课程下载&#xff1a;https://do…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的稻田虫害检测系统详解(深度学习+Python代码+UI界面+训练数据集)

摘要&#xff1a;本篇文章深入探讨了如何利用深度学习技术开发一个用于检测稻田虫害的系统&#xff0c;并且分享了完整的实现过程和资源代码下载。该系统采用了当前的YOLOv8、YOLOv7、YOLOv6、YOLOv5算法&#xff0c;对其进行了性能对比&#xff0c;包括mAP、F1 Score等关键指标…

java中xml概述

1.xml 1.1概述【理解】 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年&#xff0c;又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者&#xff1a; Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域最具权威和影响力的国际中立性技术标准机构。 到目前为…

Linux认识与学习BASH

Linux认识与学习BASH 认识BASH这个Shellshell是什么系统的合法shell与/etc/shells功能Bash Shell的功能查询命令是否为Bash shell 的内置命令(type)命令的执行与快速编辑按钮 shell的变量功能什么是变量&#xff1f;变量的使用与设置&#xff1a;echo、变量设置规则、unset环境…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Slider)

滑动条组件&#xff0c;通常用于快速调节设置值&#xff0c;如音量调节、亮度调节等应用场景。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Slider(options?: SliderOption…

Linux:时间指令 - cal date

Linux&#xff1a;时间指令 - cal & date date指令cal指令 date指令 date用于以指定格式显示时间 我们先看看直接输入date指令的效果&#xff1a; [hxyiZ2zehtehrgzt3wqccrpyfZ CSDN]$ date Tue Mar 12 21:38:01 CST 2024直接输入date指令&#xff0c;得到了以 星期 月 日…

RANDOMIZE-IN-PLACE随机排列算法

给定一个长度为 n n n的数组&#xff0c;如何构造出一个随机排列呢&#xff1f;《算法导论》给了我们一个名为RANDOMIZE-IN-PLACE的随机算法&#xff0c;该算法在数组原址上进行排序&#xff0c;时间复杂度为 O ( n ) O(n) O(n)。下面本文将介绍RANDOMIZE-IN-PLACE的设计思想及…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的水下目标检测系统(深度学习模型+UI界面+训练数据集)

摘要&#xff1a;本研究详述了一种采用深度学习技术的水下目标检测系统&#xff0c;该系统集成了最新的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行了性能评估对比。该系统能够在各种媒介——包括图像、视频文件、实时视频流及批量文件中——准确地识别水…

HarmonyOS NEXT应用开发之多层嵌套类对象监听

介绍 本示例介绍使用Observed装饰器和ObjectLink装饰器来实现多层嵌套类对象属性变化的监听。 效果图预览 使用说明 加载完成后显示商品列表&#xff0c;点击刷新按钮可以刷新商品图片和价格。 实现思路 创建FistGoodsModel类&#xff0c;类对象是用Observed修饰的类Secon…

Linux运维:磁盘分区与挂载详解

Linux运维&#xff1a;磁盘分区与挂载详解 1、磁盘分区的原理2、查看系统中所有的磁盘设备及其分区信息3、进行磁盘分区&#xff08;对于sdb新磁盘&#xff09;4、格式化分区5、挂载分区&#xff08;临时挂载、永久挂载&#xff09;6、取消挂载分区7、删除分区 &#x1f496;Th…

pytorch激活函数

目录 1.激活函数由来2. 常见激活函数2.1 Sigmoid2.2 Tanh2.3 relu 1.激活函数由来 科学家对青蛙的神经元进行研究的时候发现&#xff0c;只有超过一定的阈值青蛙才会有反应&#xff0c;因此不能将多个输入做简单的加权平均&#xff0c;而需要一个阶梯函数也就是激活函数&#…

软考75-上午题-【面向对象技术3-设计模式】-设计模式的要素

一、题型概括 上午、下午题&#xff08;试题五、试题六&#xff0c;二选一&#xff09; 每一个设计模式都有一个对应的类图。 二、23种设计模式 创建型设计模式&#xff1a;5 结构型设计模式&#xff1a;7 行为设计模式&#xff1a;11 考试考1-2种。 三、设计模式的要素 3…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的烟雾检测系统详解(深度学习模型+UI界面升级版+训练数据集)

摘要&#xff1a;本研究详细介绍了一种集成了最新YOLOv8算法的烟雾检测系统&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行性能评估对比。该系统能够在包括图像、视频文件、实时视频流及批量文件中准确识别烟雾。文章深入探讨了YOLOv8算法的原理&#xff0c;提供了Pyth…

cocos2d-x-3.17 android升级 gradle NDK_DEBUG=0 -o NDK_DEBUG=1 -o cocos2dlua_shared

由于需要升级sdk版本 需要对应升级gradle版本 记录下升级内容 externalNativeBuild { ndkBuild { - //arguments NDK_DEBUG0 -o 修改成下面 arguments NDK_DEBUG0 } } debug { …

抓取Instagram数据:Fizzler库带您进入C#爬虫程序的世界

引言 在当今数字化的世界中&#xff0c;数据是无价之宝。社交媒体平台如Instagram成为了用户分享照片、视频和故事的热门场所。作为开发人员&#xff0c;我们可以利用爬虫技术来抓取这些平台上的数据&#xff0c;进行分析、挖掘和应用。本文将介绍如何使用C#编写一个简单的Ins…

JavaWeb-Maven

一、Maven概述 Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供一套标准化的项目结构提供一套标准化的构建流程&#xff08;编译&#xff0c;测试&#xff0c;打包&#xff0c;发布......&#xff09;提供一套依赖管理机制 二、Maven简…

Mysql数据库问题

一、索引 索引分类&#xff1a;主键索引&#xff0c;普通索引&#xff0c;复合索引&#xff0c;唯一索引技术名词&#xff1a;回表&#xff0c;最左匹配&#xff0c;索引覆盖&#xff0c;索引下推 二、explain 之前已有文章讲解&#xff1a;优化器-SQL语句分析与优化 这里我…

爬虫案例1

通过get请求直接获取电影信息 目标页面: https://spa6.scrape.center/在network中可以看到是通过Ajax发送的请求&#xff0c;这个请求在postman中也可以直接请求成功&#xff0c;这只是一个用来练习爬虫的&#xff0c;没有达到js逆向的过程&#xff0c;需要通过分析js 代码来获…
最新文章