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

news2024/11/25 22:27:04

一,基础定义

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

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

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

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

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

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

类模板的代码样式:

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.coloradmin.cn/o/1511876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv5改进 | 注意力篇 | 利用YOLO-Face提出的SEAM注意力机制优化物体遮挡检测(附代码 + 修改教程)

一、本文介绍 本文给大家带来的改进机制是由YOLO-Face提出能够改善物体遮挡检测的注意力机制SEAM&#xff0c;SEAM&#xff08;Spatially Enhanced Attention Module&#xff09;注意力网络模块旨在补偿被遮挡面部的响应损失&#xff0c;通过增强未遮挡面部的响应来实现这一目…

el-Switch 开关二次确认

前言 最近在做毕设&#xff0c;有个需求是点击按钮控制用户的状态是否禁用&#xff0c;就看到element有个switch组件可以改造一下&#xff0c;就上网看了一下&#xff0c;结果为了这个效果忙活了很久。。。所以说记录一下&#xff0c;让大家少踩坑。 前置条件 先看完我的需求再…

Eclipse安装springboot

Eclipse免费&#xff0c;套件丰富&#xff0c;代码开源&#xff0c;功能强大…推荐&#xff01; 1 下载eclipse: https://www.eclipse.org/downloads/download.php?file/technology/epp/downloads/release/2023-12/R/eclipse-jee-2023-12-R-win32-x86_64.zip 2 安装Spring框…

图论(蓝桥杯 C++ 题目 代码 注解)

目录 迪杰斯特拉模板&#xff08;用来求一个点出发到其它点的最短距离&#xff09;&#xff1a; 克鲁斯卡尔模板&#xff08;用来求最小生成树&#xff09;&#xff1a; 题目一&#xff08;蓝桥王国&#xff09;&#xff1a; 题目二&#xff08;随机数据下的最短路径&#…

Hadoop学习2:完全分布集群搭建

文章目录 Fully-Distributed Operation&#xff08;完全分布模式&#xff09; 重点机器环境同步集群规划配置文件修改以及同步步骤0&#xff1a;下面其他步骤涉及修改配置以这里为准&#xff08;要不然部署使用过程会有很多问题&#xff09;通用配置&#xff08;三台节点机器&a…

社交革命的引领者:探索Facebook如何改变我们的生活方式

1.数字社交的兴起 随着互联网的普及&#xff0c;社交媒体成为我们日常生活的重要组成部分。Facebook作为其中的先驱&#xff0c;从最初的社交网络演变成了一个拥有数十亿用户的全球化平台。它不仅改变了我们与世界互动的方式&#xff0c;还深刻影响了我们的社交习惯、人际关系以…

华为机考:HJ102 字符统计

华为机考&#xff1a;HJ102 字符统计 描述 方法1 先将所有字符计算数量&#xff0c;在对比其中字符的assic码 #include<iostream> #include<vector> #include<algorithm> #include<string> using namespace std; bool cmp(pair<char, int> a,…

C++ std::list的merge()使用与分析

看到《C标准库第2版》对list::merge()的相关介绍&#xff0c;令我有点迷糊&#xff0c;特意敲代码验了一下不同情况的调用结果。 《C标准库第2版》对list::merge()的相关介绍 list::merge()定义 merge()的作用就是将两个list合并在一起&#xff0c;函数有2个版本&#xff1a;…

数字图像处理-空间滤波

空间滤波 空域滤波基础 – 离散卷积的边缘效应 平滑空间滤波器 # -*- coding: utf-8 -*- # Author: Huazhong Yang # Email: cjdxyhz163.com # Time : 2024/3/7 20:26import cv2 import numpy as np# 读取图像 image cv2.imread(a1.png)# 应用高斯滤波 # 第二个参数是高斯…

微信小程序开发系列(三十)·小程序本地存储API·同步和异步的区别

目录 1. 同步API 1.1 getStorageSync存储API 1.2 removeStorageSync获取数据API 1.3 removeStorageSync删除 1.4 clearStorageSync清空 2. 异步API 2.1 setStorage存储API 2.2 getStorage获取数据API 2.3 removeStorage删除API 2.4 clearStorage清空 3. …

qt vs 编程 字符编码 程序从源码到编译到显示过程中存在的字符编码

理解字符编码&#xff0c;请参考&#xff1a;unicode ucs2 utf16 utf8 ansi GBK GB2312 CSDN博客 汉字&#xff08;或者说多字节字符&#xff09;的存放需求&#xff0c;是计算机中各种编码问题的最直接原因。如果程序不直接使用汉字&#xff0c;或间接在所有操作步骤中统一使…

Hilt

1.使用Hilt实现快速依赖注入 1.1 导入依赖 //hilt依赖//Hiltimplementation("com.google.dagger:hilt-android:2.44")annotationProcessor("com.google.dagger:hilt-android-compiler:2.44")1.2 在build.gradle(app)中加入插件 plugins {id("com.an…

大规模自动化重构框架--OpenRewrite浅析

目录 1. OpenRewrite是什么&#xff1f;定位&#xff1f; 2. OpenWrite具体如何做&#xff1f; 3. 核心概念释义 3.1 Lossless Semantic Trees (LST) 无损语义树 3.2 访问器&#xff08;Visitors&#xff09; 3.3 配方&#xff08;Recipes&#xff09; 4. 参考链接 Open…

SpringBlade error/list SQL 注入漏洞复现

0x01 产品简介 SpringBlade 是一个由商业级项目升级优化而来的 SpringCloud 分布式微服务架构、SpringBoot 单体式微服务架构并存的综合型项目。 0x02 漏洞概述 SpringBlade 框架后台 /api/blade-log/error/list路径存在SQL注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数…

Redis应用缓存

目录 前言 关于“二八定律” 使用Redis作为缓存 为什么关系型数据库性能不高 为什么并发量高了就容易宕机 Redis就是一个用来作为数据库缓存的常见方案 缓存更新策略 定期生成 搜索引擎为例 实时生成 淘汰策略 FIFO(First In First Out) 先进先出 lRU(Least …

106. Dockerfile通过多阶段构建减小Golang镜像的大小

我们如何通过引入具有多阶段构建过程的Dockerfiles来减小Golang镜像的大小&#xff1f; 让我们从一个通用的Dockerfile开始&#xff0c;它负责处理基本的事务&#xff0c;如依赖项、构建二进制文件、声明暴露的端口等&#xff0c;以便为Go中的一个非常基础的REST API提供服务。…

YoloV8实战:YoloV8-World应用实战案例

摘要 YOLO-World模型确实是一个突破性的创新&#xff0c;它结合了YOLOv8框架的实时性能与开放式词汇检测的能力&#xff0c;为众多视觉应用提供了前所未有的解决方案。以下是对YOLO-World模型的进一步解读&#xff1a; 模型架构与功能 YOLO-World模型充分利用了YOLOv8框架的…

剑指offer面试题34:在二叉树中和为某一值的路径

面试题34&#xff1a;在二叉树中和为某一值的路径 题目&#xff1a; LCR 153. 二叉树中和为目标值的路径 - 力扣&#xff08;LeetCode&#xff09; 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路…

C语言 - 各种自定义数据类型

1.结构体 把不同类型的数据组合成一个整体 所占内存长度是各成员所占内存的总和 typedef struct XXX { int a; char b; }txxx; txxx data; typedef struct XXX { int a:1; int b:1; …

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

富文本组件&#xff0c;解析并显示HTML格式文本。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件无法根据内容自适应设置宽高属性&#xff0c;需要开发者设置显示布局。 子组件 不包含子组…