栈和堆简介
栈(Stack) 特点:
- 先进后出(
LIFO
,Last In First Out
):栈遵循后进先出
原则,意味着最后压入栈的数据最先弹出。 - 内存分配:栈由操作系统
自动管理
,内存分配和释放非常迅速。 - 生命周期:栈内存通常在函数调用时
自动分配
,函数执行完后自动释放
。 - 大小限制:栈的大小相对
较小
,由系统规定,容易发生栈溢出(stack overflow)。
用途:
- 局部变量:在函数中定义的
局部变量
通常分配在栈上。 - 函数调用管理:栈用于存储函数的
调用信息
,包括参数
、返回地址
、局部变量
等。
堆(Heap) 特点:
- 先进先出(
FIFO
,First In First Out
):堆是一个相对较大、灵活的内存区域,内存分配和释放由程序员控制
,动态
分配,分配顺序不固定
。 - 内存分配:堆内存分配和释放较为复杂,需要程序员
手动管理
(例如 C/C++ 中使用malloc
和free
)。 - 生命周期:堆内存分配后
不会自动释放
,除非程序显式释放
。否则,可能发生内存泄漏。 - 大小限制:堆的大小通常
较大
,只受限于系统的物理内存。
用途:
- 动态内存分配:堆用于存储需要
动态分配
和长时间存在
的数据,如动态数组
、链表
、树
等。 长时间存活
的数据:例如,当数据需要在多个函数之间共享时,通常会存放在堆上。
模板类的示例-栈
模板类
最常用的就是作为容器类
。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;
}
创建模板类的方法
- 先写一个普通类,使用具体的数据类型
- 调试普通类
- 把普通类改为模板类
感谢浏览,一起学习!