C++ 泛编程 —— 模板类的示例(栈)

栈和堆简介

栈(Stack) 特点:

  • 先进后出(LIFO, Last In First Out):栈遵循后进先出原则,意味着最后压入栈的数据最先弹出。
  • 内存分配:栈由操作系统自动管理,内存分配和释放非常迅速。
  • 生命周期:栈内存通常在函数调用时自动分配,函数执行完后自动释放
  • 大小限制:栈的大小相对较小,由系统规定,容易发生栈溢出(stack overflow)。

用途:

  • 局部变量:在函数中定义的局部变量通常分配在栈上。
  • 函数调用管理:栈用于存储函数的调用信息,包括参数返回地址局部变量等。

堆(Heap) 特点:

  • 先进先出(FIFO, First In First Out):堆是一个相对较大、灵活的内存区域,内存分配和释放由程序员控制动态分配,分配顺序不固定
  • 内存分配:堆内存分配和释放较为复杂,需要程序员手动管理(例如 C/C++ 中使用mallocfree)。
  • 生命周期:堆内存分配后不会自动释放,除非程序显式释放。否则,可能发生内存泄漏。
  • 大小限制:堆的大小通常较大,只受限于系统的物理内存。

用途:

  • 动态内存分配:堆用于存储需要动态分配长时间存在的数据,如动态数组链表等。
  • 长时间存活的数据:例如,当数据需要在多个函数之间共享时,通常会存放在堆上。

模板类的示例-栈

模板类最常用的就是作为容器类。C++标准库:栈、数组、链表、二叉树和哈希表,都是用模板类实现的。

进栈出栈

示例代码:

#include <iostream>
using namespace std;

class Stack // 小写的stack是C++标准库的栈,为了避免重复,此处使用大写的Stack
{
public:
    int* items; // 栈数组
    int stacksize; // 栈大小
    int top; // 栈顶指针
public:
    Stack(int size):stacksize(size), top(0) {
        // top(0) 表示栈顶指针初始化为0,即栈为空。
        items = new int[stacksize]; // 分配栈数组内存
    }
    ~Stack() {
        delete [] items; items = nullptr;
    }

    bool isEmpty() { // 判断栈是否为空
        return top == 0;
    }
    bool isFull() { // 判断栈是否已满
        return top == stacksize;
    }
    bool push(const int& item) { // 入栈操作
        // 如果栈未满,则可入栈
        if(top < stacksize) {items[top++] = item; return true;}
        // top++ 的意义:先使用 top 的当前值,然后将其递增,确保栈顶索引始终指向下一个可用位置。
        return false;
    }
    bool pop(int& item) { // 出栈操作
		// 如果栈非空,则可出栈
        if(top > 0) {item = items[--top]; return true;}
        // 先将 top 的值减 1,意味着将栈顶索引向下移动一位。
		// 注意这是前置递减,所以 top 的值在数组访问之前已经减少。
        return false;
    }
};

int main() {
    // 创建栈对象
    Stack s(5);
    // 元素入栈
    s.push(111); s.push(222); s.push(333); s.push(444); s.push(555);
    // 元素出栈
    int item;
    while(s.isEmpty() == false) {
        s.pop(item); cout << item << " 出栈" << endl;
    }
 
    return 0;
}

编译运行的结果如下:

555 出栈
444 出栈
333 出栈
222 出栈
111 出栈

再举个例子,助于理解,假设栈状态是:栈容量(stacksize):5,当前栈内容:items = [10, 20, 30, _, _](假设 _ 表示未赋值),当前 top = 3(栈中有 3 个元素)。
现在执行出栈操作:
判断是否为空:

  • top = 3,满足 top > 0,说明栈中有元素。

执行出栈操作:

  • '--top:先将 top 减少 1,变成 top = 2。
  • 取出元素:获取 items[2] 的值,即 30,赋值给 item。
  • 栈状态更新:items = [10, 20, 30, _, _](注意:30 仍然在数组中,但对栈来说它已被移除,因为 top 的值变成了 2)。

返回 true,表示出栈成功。

typedef 定义数据类型

当前这段代码中的类只支持整数,如果没有类模板技术,为了支持其他数据类型,并尽可能少改动代码,会用typedef int DataType定义栈元素的数据类型,再把全部与栈元素数据类型有关的代码都替换:

#include <iostream>
using namespace std;

typedef int DataType;

class Stack // 小写的stack是C++标准库的栈
{
public:
    DataType* items; // 栈数组
    int stacksize; // 栈大小
    int top; // 栈顶指针
public:
    Stack(int size):stacksize(size), top(0) {
        // top(0) 表示栈顶指针初始化为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;
    }
};

int main() {
    // 创建栈对象
    Stack s(5);
    // 元素入栈
    s.push(111); s.push(222); s.push(333); s.push(444); s.push(555);
    // 元素出栈
    DataType item;
    while(s.isEmpty() == false) {
        s.pop(item); cout << item << " 出栈" << endl;
    }
 
    return 0;
}

这么做的好处就是,如果要修改栈的数据类型,其他的代码不用改,只需修改typedef [数据类型] DataType以及main()函数中的s.push()即可,比如:

// int 换成 string
typedef string DataType;
...
// main()函数按照如下修改
s.push("ffff"); s.push("yyy"); s.push("dddd"); s.push("xxxx"); s.push("噼里啪啦");

编译运行的结果如下:

噼里啪啦 出栈
xxxx 出栈
dddd 出栈
yyy 出栈
ffff 出栈

栈的模板类

示例代码:

#include <iostream>
using namespace std;

template <class DataType> // 在类的声明上面一行,加上模板头部标签
class Stack
{
public:
    DataType* items;
    int stacksize;
    int top;
public:
    Stack(int size):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;
    }
};

int main() {
    Stack <string> s(5); // 用模板类创建栈对象时,需要指明数据类型
    s.push("ffff"); s.push("yyy"); s.push("dddd"); s.push("xxxx"); s.push("噼里啪啦");
    string item; // 指明存放出栈元素的临时变量的数据类型
    while(s.isEmpty() == false) {
        s.pop(item); cout << item << " 出栈" << endl;
    }
 
    return 0;
}

创建模板类的方法

  • 先写一个普通类,使用具体的数据类型
  • 调试普通类
  • 把普通类改为模板类

C++ 泛编程 —— 模板类(基本概念)

C++ 泛编程 —— 模板类的示例(数组)

感谢浏览,一起学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值