链式栈StackT
C++关键词:内部类/模板类/头插
C++自学精简教程 目录(必读)
C++数据结构与算法实现(目录)
栈的内存结构
空栈:
有一个元素的栈:
多个元素的栈:
成员函数说明
0 clear 清空栈
clear 函数负责将栈的对内存释放,成员初始化为初始值,比如指针为空指针,计数成员变量赋0值。
1 copy 从另一个栈拷贝
copy 函数可以给 拷贝构造函数调用,也可以被 赋值操作调用。
由于拷贝构造函数发生在构造阶段,对象刚刚创建,不可能有内容,而赋值操作符就不一定了。
对象被赋值的时候,可能已经有元素了,所以这时候copy 内部需要先调用 clear 成员函数来清空自己管理的堆内存。让对象重新回到一个空的栈状态。
2 pop 弹出栈顶元素
pop 执行的时候,不需要检查栈是否为空。用户应该去调用 empty来检查栈是否为空。
或者用户确定调用pop的时候栈是不可能为空的,这样就避免了不必要的代码的执行。
这样做的注意目的是为了效率,类的接口各司其职,分工明确。
接口与测试用例
#include <iostream>
#include <iomanip>//------下面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <utility>
using namespace std;
struct Record { Record(void* ptr1, size_t count1, const char* location1, int line1, bool is) :ptr(ptr1), count(count1), line(line1), is_array(is) { int i = 0; while ((location[i] = location1[i]) && i < 100) { ++i; } }void* ptr; size_t count; char location[100] = { 0 }; int line; bool is_array = false; bool not_use_right_delete = false; }; bool operator==(const Record& lhs, const Record& rhs) { return lhs.ptr == rhs.ptr; }std::vector<Record> myAllocStatistic; void* newFunctionImpl(std::size_t sz, char const* file, int line, bool is) { void* ptr = std::malloc(sz); myAllocStatistic.push_back({ ptr,sz, file, line , is }); return ptr; }void* operator new(std::size_t sz, char const* file, int line) { return newFunctionImpl(sz, file, line, false); }void* operator new [](std::size_t sz, char const* file, int line)
{return newFunctionImpl(sz, file, line, true);
}void operator delete(void* ptr) noexcept { Record item{ ptr, 0, "", 0, false }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) { auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; } else { myAllocStatistic[ind].count = 0; }std::free(ptr); } }void operator delete[](void* ptr) noexcept { Record item{ ptr, 0, "", 0, true }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) { auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (!itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; } else { myAllocStatistic[ind].count = 0; }std::free(ptr); } }
#define new new(__FILE__, __LINE__)
struct MyStruct { void ReportMemoryLeak() { std::cout << "Memory leak report: " << std::endl; bool leak = false; for (auto& i : myAllocStatistic) { if (i.count != 0) { leak = true; std::cout << "leak count " << i.count << " Byte" << ", file " << i.location << ", line " << i.line; if (i.not_use_right_delete) { cout << ", not use right delete. "; } cout << std::endl; } }if (!leak) { cout << "No memory leak." << endl; } }~MyStruct() { ReportMemoryLeak(); } }; static MyStruct my; void check_do(bool b, int line = __LINE__) { if (b) { cout << "line:" << line << " Pass" << endl; } else { cout << "line:" << line << " Ohh! not passed!!!!!!!!!!!!!!!!!!!!!!!!!!!" << " " << endl; exit(0); } }
#define check(msg) check_do(msg, __LINE__);
//------上面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------//2020-07-09
template<typename T>
class Stack
{
public:Stack(void);Stack(const Stack& _stack);Stack& operator=(const Stack& _stack);~Stack(void);public:inline const T& top(void) const;inline bool empty(void) const;inline size_t size(void) const;void push(const T& _item);void pop(void);void clear(void);
private:void copy(const Stack& stack1);
private:struct CStackitem{public:CStackitem(void);CStackitem(const T& _data, CStackitem* next = nullptr);public:CStackitem(CStackitem& _item) = delete;// = delete 表示禁止编译器生成默认版本的函数,主要用来禁止该类型对象拷贝CStackitem& operator=(CStackitem& _item) = delete;public:CStackitem* next = nullptr;//这里的初始化会在所有构造函数执行之前先执行,所以构造函数里就不用再对该成员初始化了T data;};
private:CStackitem m_head;//注意这里不是指针类型size_t m_size = 0;
};template<typename T>
Stack<T>::CStackitem::CStackitem(void)
//(1) your code 对1个成员变量初始化{
}template<typename T>
Stack<T>::CStackitem::CStackitem(const T& _data, CStackitem* _next):data(_data), next(_next)
{
}
template<typename T>
Stack<T>::Stack(void)
//(3) your code 对1个成员变量初始化{
}template<typename T>
Stack<T>::Stack(const Stack& _stack)
{//(4) your code 使用 copy 即可}template<typename T>
Stack<T>& Stack<T>::operator=(const Stack& _stack)
{//(5) your code 记得判断同一个对象赋值给自己return *this;
}template<typename T>
Stack<T>::~Stack(void)
{clear();
}
template<typename T>
bool Stack<T>::empty(void) const
{return m_size == 0;
}
template<typename T>
void Stack<T>::pop(void)
{//(9) your code 注意对象获取成员用"."操作符,指针获取成员用"->"操作符}
template<typename T>
void Stack<T>::clear(void)
{//(6) your code 可以利用 pop 来实现}
template<typename T>
void Stack<T>::copy(const Stack& from)
{//(7) your code 请先使用 clear ,再遍历链表来实现}
template<typename T>
size_t Stack<T>::size(void) const
{return m_size;
}
template<typename T>
void Stack<T>::push(const T& item)
{//(8) your code, 注意 这样写新创建的节点 CStackitem* p = new CStackitem(item, first);}
template<typename T>
const T& Stack<T>::top(void) const
{return m_head.next->data;
}int main(int argc, char** argv)
{Stack<int> stack1;check(stack1.size() == 0);stack1.push(1);check(stack1.size() == 1);auto stack2 = stack1;auto top = stack2.top();check(top == 1);check(stack2.size() == 1);stack1 = stack2;// 1 and 1stack1.push(2);// 2 1top = stack2.top();check(top == 1);check(stack1.size() == 2);check(stack1.top() == 2);stack1.clear();check(stack1.size() == 0 && stack1.empty());for (size_t i = 0; i < 10; i++){stack1.push(i);}while (!stack1.empty()){std::cout << stack1.top() << " ";stack1.pop();}cout << endl;check(stack1.size() == 0 && stack1.empty());//copy constructor{Stack<int> from;from.push(1);from.push(2);Stack<int> to(from);check(to.size() == 2);check(to.top() == 2);to.pop();check(to.top() == 1);to.pop();check(to.empty());}
}
输出:
line:155 Pass
line:157 Pass
line:160 Pass
line:161 Pass
line:165 Pass
line:166 Pass
line:167 Pass
line:169 Pass
9 8 7 6 5 4 3 2 1 0
line:180 Pass
line:188 Pass
line:189 Pass
line:191 Pass
line:193 Pass
Memory leak report:
No memory leak.
还没完!
现在我们来思考一个更具价值的问题:栈有必要重新实现一次吗?答案是否定的。
回忆我们之前的工作,我们实现了动态数组vector和链表list,这两个容器都支持在末尾增加和删除元素。
这正是栈的功能。
也就是说我们其实已经实现过栈了。
我们可以直接把动态数组和链表替换任何需要栈的地方,只不过类型的名字还不叫栈而异。
那么我们该怎么做才比较好呢?
那就是利用已经实现的容器包装出另一个容器。具体做法就是,假如我们打算用链表来实现栈(当然用数组实现也是类似的)。
可以把一个链表对象作为栈的成员变量。
对栈的操作都通过栈的成员函数转发给这个链表来做。
这种做法的好处:
(1)稳定!稳定!稳定!
因为链表的实现中有大量的细节,很容易出错。如果我们也在链表中再来一遍的话,指不定又会写出bug来。这在正式开发环境中代价是极其高昂的。没有客户愿意接受一个经常喜欢出洋相的产品。
让测试人员从头开始测试一遍产品他们的工作量几乎要翻倍。这会极大的资源浪费。竞争对手可能已经跑在了前面。
原来对链表的测试用例已经把链表的稳定性保证了,所以现在的不确定性只是在栈对链表的包装上。
由于包装的代码就是接口转发,只要类型写对,接口名别调用错了就可以了,所以出问题的概率极大的降低了。
这种以基础容器制造其他容器的做法在软件开发中叫模块化封装。
链表是一个模块,栈的是一个模块。用链表封装出了一个栈。
(2)减少开发工作量
由于使用了现成的代码,所以有些底层的代码直接拿来用,这就节省了工作量。提高了开发效率。
祝你好运!
答案在此
链式栈StackT(答案)_C++开发者的博客-CSDN博客
相关文章:

链式栈StackT
C关键词:内部类/模板类/头插 C自学精简教程 目录(必读) C数据结构与算法实现(目录) 栈的内存结构 空栈: 有一个元素的栈: 多个元素的栈: 成员函数说明 0 clear 清空栈 clear 函数负责将栈的对内存释放…...

Fiddler中 AutoResponder 使用
Fiddler的 AutoResponder ,即URL重定向功能非常强大。不管我们做URL重定向,还是做mock测试等,都可以通过该功能进行实践。 下面,小酋就来具体讲下该功能的用法。 Enable rules 启用规则Unmatched requests passthrough 没有匹配…...

77GHz线性调频连续波雷达
文章目录 前言 一、背景 二、优缺点 三、工作原理 四、电路模块设计 4.1.LFMCW信号源 4.2.发射电路 4.3.接收电路 4.4.信号处理器 五、应用 5.1.汽车测距 5.2.军事方面 5.3.气象方面 总结 前言 这篇文章是博主本科期间整理的关于77GHz线性调频连续波雷达的相关资料,…...

YOLOV8改进:更换为MPDIOU,实现有效涨点
1.该文章属于YOLOV5/YOLOV7/YOLOV8改进专栏,包含大量的改进方式,主要以2023年的最新文章和2022年的文章提出改进方式。 2.提供更加详细的改进方法,如将注意力机制添加到网络的不同位置,便于做实验,也可以当做论文的创新点。 2.涨点效果:更换为MPDIOU,实现有效涨点! 目录…...

BookStack开源免费知识库docker-compose部署
BookStack(书栈)是一个功能强大且易于使用的开源知识管理平台,适用于个人、团队或企业的文档协作和知识共享。 一、BookStack特点 简单易用:BookStack提供了一个直观的用户界面,使用户能够轻松创建、编辑和组织文档多…...

Linux:编译遇到 Please port gnulib freadahead.c to your platform ,怎么破
问题背景 编译m4时遇到以下错误,该怎么解决呢? 解决方法 进入m4的build目录:build/host-m4-1.4.17 输入命令: sed -i s/IO_ftrylockfile/IO_EOF_SEEN/ lib/*.c echo "#define _IO_IN_BACKUP 0x100" >> lib/std…...

three.js(三):three.js的渲染结构
three.js 的渲染结构 概述 three.js 封装了场景、灯光、阴影、材质、纹理和三维算法,不必在直接用WebGL 开发项目,但有的时候会间接用到WebGL,比如自定义着色器。three.js 在渲染三维场景时,需要创建很多对象,并将它…...

客户端读写HBase数据库的运行原理
1.HBase的特点 HBase是一个数据库,与RDMS相比,有以下特点: ① 它不支持SQL ② 不支持事务 ③ 没有表关系,不支持JOIN ④ 有列族,列族下可以有上百个列 ⑤ 单元格,即列值,可以存储多个版本的值&…...

不使用VH6501设备,通过VN1630等普通设备使用canConfigureBusOff函数进行busoff干扰测试
** 特别注意一下,使用这个函数需要你的vector驱动在9.6以上以及支持 ISO CAN FD. ** 函数canConfigureBusOff 可以通过脚本的形式产生bus off,而VH6501可以通过干扰bit位来产生bus off(使用CANoe Demo - CANDisturbanceMain进行Bus Off测试)。 对于函数canConfigureBusOf…...

服务器数据恢复-服务器RAID6硬盘故障离线的数据恢复案例
服务器数据恢复环境: 服务器中有一组由6块磁盘组建的RAID6磁盘阵列。服务器作为WEB服务器使用,上面运行了MYSQL数据库以及存放了网站代码和其他数据文件。 服务器故障: 在服务器运行过程中该raid6阵列中有两块磁盘先后离线,但是管…...
DB2 HADR+TSA运维,TSA添加资源组的命令
Tivoli System Automation(TSA)是一个高可用性集群管理软件,DB2 TSAHADR高可用方案可以实现DB2 hadr主备的自动检测切换。本文详细介绍了TSA的常用命令,如何把CDC或者DSG添加到TSA集群中,以及TSA的错误分析方法 常用命令…...
LeetCode-135-分发糖果
题目描述:n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求,给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果,计…...
Viva Workplace Analytics Employee Feedback SU Viva Glint部署方案
目录 一、Viva Workplace Analytics & Employee Feedback SU Viva Glint介绍 二、Viva Glint和Viva Pulse特点和优势 1. 简单易用...
ASIC-WORLD Verilog(14)系统任务
写在前面 在自己准备写一些简单的verilog教程之前,参考了许多资料----Asic-World网站的这套verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加点自己的理解)分享给大家。 这是网站原文&…...

两台电脑共享文件设置
步骤一:确保网络连接正常,可网线直连。 两台电脑IP设置,例: 步骤二:启用共享功能。 1.在【控制面板】中选择【网络和Internet】; 2.点击【网络和共享中心】,在左侧导航栏中,点击【…...
《C和指针》笔记17:sizeof
sizeof操作符判断它的操作数的类型长度,以字节为单位表示。 操作数既可以是个表达式(常常是单个变量), sizeof x上面的式子返回变量x所占据的字节数。 也可以是两边加上括号的类型名。 sizeof(int)上面的式子返回整型变量的字…...

说说大表关联小表
分析&回答 Hive 大表和小表的关联 优先选择将小表放在内存中。小表不足以放到内存中,可以通过bucket-map-join(不清楚的话看底部文章)来实现,效果很明显。 两个表join的时候,其方法是两个join表在join key上都做hash bucket,…...

Unity 之 方括号[ ] 的用法以及作用
文章目录 在Unity中,方括号 [ ] 通常用于表示属性、特性(Attributes)或者元数据(Metadata)。这些标记提供了附加信息,可以用于修改类、方法、字段等的行为或者在编辑器中进行设置。 以下是一些常见的用法&…...
微服务nacos或者yml配置内容部分加密jasypt
写在最前:因业务需要把nacos配置中的部分密码加密,不能暴露在外,本想用nacos官方的插拔插件nacos-aes-encryption-plugin的,但是比较复杂且官方文档说的不清不楚所以弃用,有兴趣的可以参考。链接:https://n…...

Vue:插槽,与自定义事件
1.插槽slot <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <div id"app"><!-- <p>列表书籍</p>--> <!-- …...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...

Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...