链式栈StackT

news2024/11/26 2:31:12

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 1
    stack1.push(2);// 2 1
    top = 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博客

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

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

相关文章

[js逆向补环境专栏]过xhs的x2 x-s环境检测 -- part1

[补环境]过xhs的x2环境检测 – part1 Xhs的jsvmp用算法逆向确实容易头秃&#xff0c;扣代码对vmp而言也用处不大&#xff0c;此时补环境的重要性就出来了&#xff0c;通过把运行js的环境伪造得像浏览器一样&#xff0c;就能模拟出好像请求都是通过浏览器发起的一样。 这里要区…

光模块温度报警的原因及解决措施

光模块是数据中心网络中的关键组件&#xff0c;它们在高温环境下可能会受到影响。如果光模块的温度超过正常范围&#xff0c;可能会导致网络故障和光模块损坏。因此&#xff0c;了解光模块的温度报警并采取相应的解决措施非常重要。 一、光模块温度报警的原因 光模块温度报警通…

Linux之虚拟主机功能

目录 虚拟主机功能 概述 基于 IP 地址的虚拟主机 原理 案例 --- 增加多个IP地址&#xff0c;实现基于不同IP地址的虚拟主机功能 基于端口号的虚拟主机 原理 案例 --- 基于不同端口号的虚拟主机 基于域名的虚拟主机 原理 域名解析 案例 --- 使用2个域名建立虚拟主机网…

2023年6月GESP C++ 二级试卷解析

2023年6月GESP C 二级试卷解析 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 1.高级语言编写的程序需要经过以下&#xff08; &#xff09;操作&#xff0c;可以生成在计算机上运行的可执行代码。 A.编辑 B.保存 C.调试 D.编译 【答案】D 【考纲知识点…

问ChatGPT如何优雅地分手/离婚,然后由心理学专家验证它说对了什么

ChatGPT是一种由AI驱动的写作生成器&#xff0c;它利用互联网上的各种信息回复人类问题与要求。它可以回答问题、讲笑话、创意写作和整理研究。 ChatGPT可以取代艺术家吗&#xff1f;ChatGPT可以创作交响曲吗&#xff1f;ChatGPT的内容是否都合乎伦理&#xff1f;面对AI的完全…

iTOP-2K1000开发板挂载U盘和固态到开发板

1.挂载固态硬盘分区 输入命令 mount /dev/sdb1 /mnt/将固态硬盘的分区挂载到/mnt 目录下&#xff0c;/dev/sdb1 为开发板实际识 别的固态硬盘节点 &#xff0c;大家一定要根据自己开发板的实际情况决定&#xff0c;如下图所示&#xff1a; 2.挂载 U 盘分区 输入命令 fdisk -…

无涯教程-Android - RadioGroup函数

RadioGroup类用于单选按钮集。 如果我们选中属于某个单选按钮组的一个单选按钮,它将自动取消选中同一组中以前选中的任何单选按钮。 RadioGroup属性 以下是与RadioGroup控制相关的重要属性。您可以查看Android官方文档以获取属性的完整列表以及可以在运行时更改这些属性的相关…

【数据库技术】NineData数据复制,加速实时数仓构建

8 月 30 日&#xff0c;由 NineData 和 SelectDB 共同举办的主题为“实时数据驱动&#xff0c;引领企业智能化数据管理”的线上联合发布会&#xff0c;圆满成功举办&#xff01;双方聚焦于实时数据仓库技术和数据开发能力&#xff0c;展示如何通过强大的生态开发兼容性&#xf…

项目:智慧教室(cubemx+webserver)

一。项目需求 二。实现外设控制 1.cubemx创建工程 &#xff08;1&#xff09;RCC配置为外部时钟&#xff0c;修改时钟参数 &#xff08;2&#xff09;SWD配置&#xff1a;为了不引脚冲突&#xff0c;使用 &#xff08;3&#xff09;串口的使用&#xff08;USART1&#xff0c;…

打破时空限制:海外网红营销中的直播与虚拟互动

随着全球互联网的普及和技术的迅速发展&#xff0c;传统的商业模式也在不断演变。其中&#xff0c;海外网红直播带货成为了一种引人注目的全新营销方式。近年来&#xff0c;虚拟技术的蓬勃发展为海外网红直播带货提供了新的可能性&#xff0c;不仅丰富了消费者的购物体验&#…

2023UTONMOS斯珂塔全球电竞大赛在粤举办

8月28日&#xff0c;2023 UTONMOS斯珂塔全球电竞大赛于中国广东举办。 此次大赛是由上海和数信息科技集团有限公司与广东关氏集团共同主办的全球性电子竞技盛会&#xff0c;其汇聚国内外头部行业嘉宾、全球的电竞爱好者参与&#xff0c;以和数集团旗下的3D国风玄幻MMORPG链游《…

【遮天】李小曼回归,新形象无差云曦,短板竟是身材?

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析遮天 最新一集《遮天》已经更新&#xff0c;在成功卖掉段德之后&#xff0c;叶凡便离开妖帝坟冢&#xff0c;毕竟他身上拥有庞博从妖帝坟冢带出来的道经和被誉为中州至宝的绿铜 虽然这两样物品都在叶凡的苦海中&#xff0…

轴向磁通电驱动解析

轴向磁通电机的技术创新和量产应用&#xff0c;或将有效解决电动汽车领域目前所面临的一些突出难题&#xff0c;比如轻量化、扭矩密度和人们最为关心的续航里程等。在奔驰汽车刚刚发布的Vision One Eleven概念车&#xff0c;以及此前已经面世的法拉利SF90 Stradale、296GTB和迈…

突破5G封锁!逆风翻盘!华为新机Mate60王者归来 | 百能云芯

外界关注的华为Mate60系列手机「低调」上线&#xff0c;于29日突然开卖最新款旗舰手机Mate 60 Pro&#xff0c;多项实测数据显示&#xff0c;这台智能手机支持5G网络&#xff0c;意味华为与中芯国际携手突破美国的5G芯片封锁。但华为拒绝证实&#xff0c;表示要等9月发布会。 华…

c++图论免费ppt,简单深度理解图论

本篇博文想分享一个ppt,是帮助大家简单深度理解c图论. 作者承诺&#xff1a;分享的东西没有病毒&#xff0c;是资料。 分享的东西一个是ppt,ppt里面是150页的&#xff0c;里面将带领大家简单深度理解c图论&#xff0c;还有一个就是里面例题的数据&#xff0c;大家可以按照数据…

5年测试在职经验之谈:2年功能测试、3年自动化测试,从入门到不可自拔...

毕业3年了&#xff0c;学的是环境工程专业&#xff0c;毕业后零基础转行做软件测试。 已近从事测试行业8年了&#xff0c;自己也从事过2年的手工测试&#xff0c;从事期间越来越觉得如果一直在手工测试的道路上前进&#xff0c;并不会有很大的发展&#xff0c;所以通过自己的努…

Elasticsearch终端命令行用法大全

API作用使用场景curl localhost:9200/_cluster/health?pretty查看ES健康状态curl localhost:9200/_cluster/settings?pretty查看ES集群的设置其中persistent为永久设置&#xff0c;重启仍然有效&#xff1b;trainsient为临时设置&#xff0c;重启失效curl localhost:9200/_ca…

2023蓝帽杯初赛取证方向

案情介绍 2021年5月&#xff0c;公安机关侦破了一起投资理财诈骗类案件&#xff0c;受害人陈昊民向公安机关报案称其在微信上认识一名昵称为yang88的网友&#xff0c;在其诱导下通过一款名为维斯塔斯的APP&#xff0c;进行投资理财&#xff0c;被诈骗6万余万元。接警后&#xf…

冠达管理:市盈率公式?

市盈率是一种用来衡量公司股票价格相对于每股收益的目标。市盈率公式为市场价格除以每股收益。市盈率既能够用来衡量某个股票的价值&#xff0c;也能够用来比较不同公司之间的股票价格。本文将从市盈率的定义、计算方法、运用范围以及市盈率的局限性等多个角度来分析市盈率公式…

解决paramiko库在密码为空的情况下登陆失败的问题

发现源码client.py文件中缺少了在该情况下的逻辑进入&#xff0c;添加以下内容即可