当前位置: 首页 > news >正文

C++ 泛编程 —— 嵌套使用模板类

嵌套使用模板类

  • 嵌套使用模板类最常见的场景
  • 数组容器中有栈容器
  • 栈容器中有数组容器
  • 递归使用模板类

嵌套使用模板类最常见的场景

容器中有容器
数组元素可以是中的元素可以是数组。先来看一下StackVector的基本代码,定长数组Array的代码也给出来,但是不会用到,代码如下:

#include <iostream>
using namespace std;// 定长数组 Array
template <class T, int len=10>
class Array {
private:T* items[len];
public:Array() {}~Array() {}T& operator[](int index) {return items[index];}const T& operator[] (int index) const {return items[index];}
};// 栈 Stack
template <class DataType>
class Stack {
private:DateType* items;int stacksize;   int top;
public:Stack(int size=3): stacksize(size), top(0) {items  = new DataType[stacksize];}~Stack() {delete [] items; items = nullptr;}bool isEmpty() {return top == 0;}bool isFull() {return top == stacksize;}bool push(const DateType& item) {if(top < stacksize) {items[top++] = item; return true;}return false;}bool pop() {if(top > 0) {item = items[--top]; return true;}return false;}
};// 动态数组 Vector
template <class T>
class Vector {
privatet:int len; // 数组元素的个数T* items; // 数组元素
public:Vector(int size=2): len(size) { // 元素个数的缺省值为 2items = new T[len];}~Vector() {delete [] items; items = nullptr;}void resize(int size) {if(size <= len) return; // 只能往更大的方向扩容T* tmp = new T[size]; // 分配更大的内存空间for(int i = 0; i < len; i++) tmp[i] = items[i]; // 复制旧数组元素到新数组delete [] items; // 释放旧数组items = tmp;len = size;}int size() {return len;}T& operator [] (int index) {if(index >= len) resize(index + 1);return items[index];}const T& operator [] (int index) const {return items[index];} // 重载操作符[],不能修改数组中的元素。
};int main() {return 0;
}

数组容器中有栈容器

目前的代码中,Vector容器的大小缺省值是2Stack容器的大小缺省值是3。现在演示数组容器Vector中有栈Stack容器的情况,代码如下:

#include <iostream>
using namespace std;// 栈 Stack
template <class DataType>
class Stack {
private:DataType* items;int stacksize;   int top;
public:Stack(int size=3): stacksize(size), top(0) {items  = new DataType[stacksize];}~Stack() {delete [] items; items = nullptr;}bool isEmpty() {return top == 0;}bool isFull() {return top == stacksize;}bool push(const DataType& item) {if(top < stacksize) {items[top++] = item; return true;}return false;}bool pop(DataType& item) {if(top > 0) {item = items[--top]; return true;}return false;}
};// 动态数组 Vector
template <class T>
class Vector {
private:int len; // 数组元素的个数T* items; // 数组元素
public:Vector(int size=2): len(size) { // 元素个数的缺省值为 2items = new T[len];}~Vector() {delete [] items; items = nullptr;}void resize(int size) {if(size <= len) return; // 只能往更大的方向扩容T* tmp = new T[size]; // 分配更大的内存空间for(int i = 0; i < len; i++) tmp[i] = items[i]; // 复制旧数组元素到新数组delete [] items; // 释放旧数组items = tmp;len = size;}int size() {return len;}T& operator [] (int index) {if(index >= len) resize(index + 1);return items[index];}const T& operator [] (int index) const {return items[index];} // 重载操作符[],不能修改数组中的元素。};int main() {// 创建Vector对象vs,vs是一个Vector,Vector中存储Stack<string>类型的元素。Vector<Stack<string>> vs;// 往容器中插入数据vs[0].push("hello world!"); vs[0].push("hihihi"); vs[0].push("123456"); // vs[0]是一个栈,往vs[0]中插入数据vs[1].push("你好!"); vs[1].push("abcdef"); vs[1].push("xxxxx"); // vs[1]也是一个栈,往vs[1]中插入数据// 用嵌套循环,遍历vs中的所有元素for(int i = 0; i < vs.size(); i++) {while(vs[i].isEmpty() == false) {string item;vs[i].pop(item);cout << "pop item = " << item << endl;}}return 0;
}

运行的结果如下:

pop item = 123456
pop item = hihihi
pop item = hello world!
pop item = xxxxx
pop item = abcdef
pop item = 你好!

容器中的容器就是二维容器,但不能简单把它说成二维数组。因为不同的容器可以实现不同的数据结构。像二维数组,但不是二维数组。目前的程序,Stack是没有扩展功能的,而Vector是有resize()扩展功能的,那就往Vector容器中再多加一个元素,只入栈两个元素,其他代码不变,main()函数代码如下:

int main() {Vector<Stack<string>> vs;vs[0].push("hello world!"); vs[0].push("hihihi"); vs[0].push("123456");vs[1].push("你好!"); vs[1].push("abcdef"); vs[1].push("xxxxx");vs[2].push("Happy Everyday!"); vs[1].push("吃好喝好,身体健康!"); // 往Vector中再增加一个Stack数据for(int i = 0; i < vs.size(); i++) {while(vs[i].isEmpty() == false) {string item;vs[i].pop(item);cout << "pop item = " << item << endl;}}return 0;
}

我的电脑编译运行的结果如下,你的电脑可能编译不通过,或者编译运行的结果与我的不同,总之是不正常的:

pop item = 123456
pop item = hihihi
pop item =
pop item = xxxxx
pop item = abcdef
pop item =
pop item = Happy Everyday!
free(): double free detected in tcache 2
Aborted (core dumped)

目前这个代码异常的原因在Vector类中,具体在扩展数组内存空间的resize()函数里面。 tmp[i] = items[i];这条语句的意思是:把原数组中的数据拷贝到新数组中去。如果复制的是C++内置的数据类型,则不存在问题;如果复制的是自定义的类,并且类中使用了堆区内存,就存在浅拷贝的问题。
具体解释一下,在Stack类中,根据语句:DataType* items;可以确定items这个成员变量是指针,使用了堆区内存。这样的话,对Stack类用浅拷贝是不行的,要用深拷贝。也就是说,对于Stack这种类,一定要重写拷贝构造函数和赋值函数。在此实例中,未用到Stack类的拷贝构造函数,无需在意;但这条语句:tmp[i] = items[i];用到了Stack类的赋值函数。所以,应该为Stack类重写赋值函数,实现深拷贝。具体的做法是,在Stack类中添加如下代码:

Stack& operator = (const Stack& v) {delete [] items;stacksize = v.stacksize;items = new DataType[stacksize];for(int i = 0; i < stacksize; i++) {items[i] = v.items[i];}top = v.top;return *this;
}

再次编译运行,结果如下:

pop item = 123456
pop item = hihihi
pop item = hello world!
pop item = xxxxx
pop item = abcdef
pop item = 你好!
pop item = Happy Everyday!

栈容器中有数组容器

Vector类也加上赋值运算符的重载函数,实现深拷贝,以便演示栈容器Stack中有数组容器Vector的情况,代码如下:

#include <iostream>
using namespace std;// 栈 Stack
template <class DataType>
class Stack {
private:DataType* items;int stacksize;   int top;
public:Stack(int size=3): stacksize(size), top(0) {items  = new DataType[stacksize];}~Stack() {delete [] items; items = nullptr;}Stack& operator = (const Stack& v) {delete [] items;stacksize = v.stacksize;items = new DataType[stacksize];for(int i = 0; i < stacksize; i++) {items[i] = v.items[i];}top = v.top;return *this;}bool isEmpty() {return top == 0;}bool isFull() {return top == stacksize;}bool push(const DataType& item) {if(top < stacksize) {items[top++] = item; return true;}return false;}bool pop(DataType& item) {if(top > 0) {item = items[--top]; return true;}return false;}
};// 动态数组 Vector
template <class T>
class Vector {
private:int len; // 数组元素的个数T* items; // 数组元素
public:Vector(int size=2): len(size) { // 元素个数的缺省值为 2items = new T[len];}~Vector() {delete [] items; items = nullptr;}Vector& operator = (const Vector& v) {delete [] items;len = v.len;items = new T[len];for(int i = 0; i < len; i++) items[i] = v.items[i];return *this;}void resize(int size) {if(size <= len) return; // 只能往更大的方向扩容T* tmp = new T[size]; // 分配更大的内存空间for(int i = 0; i < len; i++) tmp[i] = items[i]; // 复制旧数组元素到新数组delete [] items; // 释放旧数组items = tmp;len = size;}int size() {return len;}T& operator [] (int index) {if(index >= len) resize(index + 1);return items[index];}const T& operator [] (int index) const {return items[index];} // 重载操作符[],不能修改数组中的元素。};int main() {// 创建Stack对象,sv是一个栈,栈中存储Vector<string>类型的元素。Stack<Vector<string>> sv;// 先创建Vector<string>对象,再插入到Stack中Vector<string> tmp;// 第一次把字符串数据放到临时容器tmp中,再第一次入栈。tmp[0] = "hi ~"; tmp[1] = "hello ~"; sv.push(tmp);// 第二次把字符串数据放到临时容器tmp中,再第二次入栈。tmp[0] = "666"; tmp[1] = "888"; sv.push(tmp);// 第二次把字符串数据放到临时容器tmp中,再第二次入栈。tmp[0] = "qqqqq"; tmp[1] = "324fwre"; tmp[2] = "09ji"; sv.push(tmp);// 用嵌套循环,遍历sv中的所有元素while(sv.isEmpty() == false) {sv.pop(tmp);for(int i = 0; i < tmp.size(); i++) {cout << "sv[" << i << "] = " << tmp[i] << endl;}}return 0;
}

运行结果如下:

sv[0] = qqqqq
sv[1] = 324fwre
sv[2] = 09ji
sv[0] = 666
sv[1] = 888
sv[0] = hi ~
sv[1] = hello ~

递归使用模板类

递归使用模板类,属于嵌套使用模板类的特殊情况,自己嵌套自己。对于以上代码,仅需修改main函数中的调用代码即可,参考如下:

int main() {// Vector嵌套VectorVector<Vector<string>> vv; // 递归使用模板类vv[0][0] = "hello"; vv[0][1] = "world"; vv[0][2] = "!";vv[1][0] = "你好"; vv[1][1] = "世界";vv[2][0] = "Happy"; vv[2][1] = "Everyday"; vv[2][2] = "!"; vv[2][3] = "吃好喝好";for(int i = 0; i <vv.size(); i++){for(int j = 0; j < vv[i].size(); j++) {cout << "vv[" << i << "][" << j << "] = " << vv[i][j] << endl;}}return 0;
}

运行结果如下:

vv[0][0] = hello
vv[0][1] = world
vv[0][2] = !
vv[1][0] = 你好
vv[1][1] = 世界
vv[2][0] = Happy
vv[2][1] = Everyday
vv[2][2] = !
vv[2][3] = 吃好喝好

注意,这跟二维数组不同二维数组是一个矩阵第二维的大小是固定的。而vv的大小不是固定的。也可以调整一下输出格式,更能直观感受vv第二维不固定的特点,代码如下:

int main() {// Vector嵌套VectorVector<Vector<string>> vv; // 递归使用模板类vv[0][0] = "hello"; vv[0][1] = "world"; vv[0][2] = "xxx";vv[1][0] = "你好"; vv[1][1] = "世界";vv[2][0] = "Happy"; vv[2][1] = "Everyday"; vv[2][2] = "qqq"; vv[2][3] = "吃好喝好";for(int i = 0; i <vv.size(); i++){for(int j = 0; j < vv[i].size(); j++) {cout << vv[i][j] << " ";}cout << endl;}return 0;
}

运行的结果如下:

hello world xxx
你好 世界
Happy Everyday qqq 吃好喝好

由此可以看出,vv的第一行有3个元素、第二行有2个元素、第三行有4个元素。故Vector<Vector<string>> vv;的第二个纬度是不固定的。

感谢浏览,一起学习!

相关文章:

C++ 泛编程 —— 嵌套使用模板类

嵌套使用模板类 嵌套使用模板类最常见的场景数组容器中有栈容器栈容器中有数组容器递归使用模板类 嵌套使用模板类最常见的场景 容器中有容器 数组的元素可以是栈&#xff0c;栈中的元素可以是数组。先来看一下Stack和Vector的基本代码&#xff0c;定长数组Array的代码也给出来…...

【WebGIS】Cesium:GLTF数据加载

在3D Web GIS开发中&#xff0c;使用GLTF格式的模型可以提高应用的加载速度并提升用户体验。Cesium.js是一个强大的3D地理空间引擎&#xff0c;支持GLTF格式的3D模型&#xff0c;并且提供丰富的API来处理和优化模型的加载和渲染。本文将系统地介绍如何加载GLTF模型&#xff0c;…...

【面经】25届 双非本科 字节跳动 北京 四年的总结

点击“硬核王同学”&#xff0c;选择“关注” 福利干货第一时间送达 大家好&#xff0c;我是硬核王同学&#xff0c;最近在做免费的嵌入式知识分享&#xff0c;帮助对嵌入式感兴趣的同学学习嵌入式、做项目、找工作&#xff01; 给大家分享一个25届本科大佬的面经&#xff0c…...

抖去推碰一碰系统技术源码/open SDK转发技术开发

抖去推碰一碰系统技术源码/open SDK转发技术开发 碰一碰智能系统#碰碰卡系统#碰一碰系统#碰一碰系统技术源头开发 碰碰卡智能营销系统开发是一种集成了人工智能和NFC技术的工具&#xff0c;碰碰卡智能营销系统通过整合数据分析、客户关系管理、自动化营销活动、多渠道整合和个…...

goview——vue3+vite——数据大屏配置系统

低代码数据大屏配置系统&#xff1a; 数据来源是可以动态api配置的&#xff1a; 配置上面的api接口后&#xff0c;在数据过滤中进行数据格式的转化。 以上内容&#xff0c;来源于https://gitee.com/dromara/go-view/tree/master-fetch/ 后端代码如下&#xff0c;需要更改…...

中间件xxl-job安装

拉取镜像 docker pull xuxueli/xxl-job-admin:2.4.2 创建xxl-job-admin容器 docker create --name xxl-job-admin -p 9099:8080 -e PARAMS"--spring.datasource.urljdbc:mysql://192.168.96.57:3306/xxl_job2Unicodetrue&characterEncodingUTF-8 --spring.dataso…...

【第2篇】 Python与数据库基础

1. 数据库的基本概念 1.1 表&#xff08;Table&#xff09; 表是数据库中存储数据的基本单位&#xff0c;由行和列组成。例如&#xff1a;users 表可以存储用户信息&#xff0c;每一行代表一个用户&#xff0c;每一列代表用户的属性&#xff08;如姓名、年龄&#xff09;。 …...

CTFHUB-web进阶-php

我们用蚁剑中的这个插件来做这些关卡 一.LD_PRELOAD 发现这里有一句话木马&#xff0c;并且把ant给了我们&#xff0c;我们直接连接蚁剑 右键 选择模式&#xff0c;都可以试一下&#xff0c;这里第一个就可以 点击开始 我们进入到目录&#xff0c;刷新一下&#xff0c;会有一个…...

深度学习使用Anaconda打开Jupyter Notebook编码

新手入门深度学习使用Anaconda打开Jupyter Notebook编码 1. 安装Anaconda 第一种是Anaconda官网下载安装包&#xff0c;但是很慢&#xff0c;不太建议 第二种使用国内清华大学镜像源下载 选择适合自己电脑的版本&#xff0c;支持windows&#xff0c;linux系统 下载完之后自行…...

金蝶V10中间件的使用

目录 环境准备搭建过程配置修改应用部署 环境准备 Linux内核服务器JDK1.8安装包&#xff1a;AAS-V10.zip程序包&#xff1a;***.war 搭建过程 将安装包上传至服务器opt目录下&#xff0c;官方给定的默认服务主目录为“/opt/AAS-V10/ApusicAS/aas/”&#xff1b;解压安装包(解…...

Firewalld 防火墙详解:深入理解与实践指南

在现代网络环境中&#xff0c;防火墙是保护系统和网络不受未授权访问的关键工具。firewalld是Linux系统中广泛使用的动态防火墙管理工具&#xff0c;它提供了强大的功能和灵活的配置选项。本文将深入探讨firewalld防火墙的工作原理、配置和管理&#xff0c;以及如何在实际环境中…...

linux系统编程(五)

1、信号 信号是事件发生时对进程的通知机制&#xff0c;针对每个信号都定义了一个唯一的整数&#xff0c;这些整数定义在signal.h中。 常见信号如下&#xff1a; SIGABRT&#xff1a;进程调用abort函数&#xff0c;系统向进程发送此信号&#xff0c;终止进程并产生核心转储文…...

Effective C++ 条款 16:成对使用 `new` 和 `delete` 时要采取相同形式

文章目录 条款 16&#xff1a;成对使用 new 和 delete 时要采取相同形式核心思想示例代码错误用法分析设计建议总结 条款 16&#xff1a;成对使用 new 和 delete 时要采取相同形式 核心思想 一致性要求 当使用 new 分配内存时&#xff0c;必须在相应的 delete 操作中保持一致&a…...

【HarmonyOS NEXT】鸿蒙原生应用“上述”

鸿蒙原生应用“上述”已上架华为应用市场&#xff0c;欢迎升级了鸿蒙NEXT系统的用户下载体验&#xff0c;用原生更流畅。 个人CSDN鸿蒙专栏欢迎订阅&#xff1a;https://blog.csdn.net/weixin_44640245/category_12536933.html?fromshareblogcolumn&sharetypeblogcolumn&a…...

【人工智能】使用Python构建推荐系统:从协同过滤到深度学习

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 推荐系统是现代互联网的重要组成部分,广泛应用于电商、社交媒体和流媒体平台中。本文详细介绍了如何使用Python构建推荐系统,从传统的协同…...

店铺营业状态设置

admineShopController RestController("admineShopController") RequestMapping("/admin/shop") Api(tags "店铺相关接口") Slf4j public class ShopController {//设置一个常量 因为经常使用public static final String KEY "SHOP-ST…...

batchnorm和layernorm的理解

batchnorm和layernorm原理和区别 batchnorm 原理 对于一个特征tensor x ∈ R b c f 1 f 2 … x \in \mathbb{R}^{b \times c \times f_1 \times f_2 \times \dots} x∈Rbcf1​f2​… 其中&#xff0c; c c c是通道&#xff0c; f f f是通道中各种特征&#xff0c;batchno…...

在git commit之前让其自动执行一次git pull命令

文章目录 背景原因编写脚本测试效果 背景原因 有时候可以看到项目的git 提交日志里好多 Merge branch ‘master’ of …记录。这些记录是怎么产生的呢&#xff1f; 是因为在本地操作 git add . 、 git commit -m "xxxxx"时&#xff0c;没有提前进行git pull操作&…...

【Rust自学】6.3. 控制流运算符-match

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 6.3.1. 什么是match match允许一个值与一系列模式进行匹配&#xff0c;并执行匹配的模式对应的代码。模式可以是字面值、变量名、通配符等…...

大模型应用技术系列(三): 深入理解大模型应用中的Cache:GPTCache

前言 无论在什么技术栈中,缓存都是比较重要的一部分。在大模型技术栈中,缓存存在于技术栈中的不同层次。本文将主要聚焦于技术栈中应用层和底层基座之间中间件层的缓存(个人定位),以开源项目GPTCache(LLM的语义缓存)为例,深入讲解这部分缓存的结构和关键实现。 完整技术…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...