栈的抽象类
template <class Type>
class stackADT
{
public:
virtual void initializeStack() = 0;
virtual bool isEmptyStack()const = 0;
virtual bool isFullStack()const = 0;
virtual void push(const Type&) = 0;
virtual void pop() = 0;
virtual Type top()const = 0;
};
抽象类只给出最基本的方法,没有函数实现。
节点
template<class Type>
struct node
{
Type info;
node<Type>* next;
};
用struct定义节点的好处在于不用管构造函数,结构体里只放变量,而且这里struct也是个模板,可以适应不同的元素类型。
链栈类
template<class Type>
class linkStack :public stackADT<Type>
{
public:
void initializeStack();
bool isEmptyStack()const;
bool isFullStack()const;
void push(const Type&);
void pop();
Type top()const;
linkStack();
linkStack(const linkStack<Type>&);
~linkStack();
const linkStack<Type>& operator=(const linkStack<Type>&);
private:
node<Type>* stackTop;
void copyStack(const linkStack<Type>&);
};
链栈继承了抽象栈的基本方法,我们要表示一个链栈只需要一个指向栈顶的指针就行了。
成员函数表
函数名 | 函数功能 |
---|---|
void isEmptyStack()const | 判断栈是否为空 |
void isFullStack()const | 判断栈是否已经满 |
void initializeStack() | 初始化栈 |
void pop() | 弹出一个元素 |
void push(const Type&) | 将元素压入栈中 |
Type top()const | 返回栈顶元素 |
linkStack() | 构造函数 |
void copyStack(const linkStack&) | 实现栈的复制操作 |
linkStack(const linkStack&) | 拷贝构造函数 |
~linkStack() | 析构函数 |
const linkStack& operator=(const linkStack&) | 重载赋值符 |
成员函数实现
linkStack()
对于一个类而言我们首先要考虑的就是其构造函数如何设计,第一个问题就来了,到底要不要在构造函数生成节点?并不需要,这和数组实现的栈不同,数组栈构造函数会开辟一段内存,而链栈只要让栈顶指针为空就好了。
代码如下:
template<class Type>
linkStack<Type>::linkStack()
{
stackTop = nullptr;
}
void isEmptyStack()const
判断链栈是否为空很简单,和数组栈类似,数组栈是看栈元素个数是否为0,链栈则是看栈顶指针是否为空。
代码如下:
template<class Type>
bool linkStack<Type>::isEmptyStack()const
{
return stackTop == nullptr;
}
void isFullStack()const
对于链栈来说理论上是永远不会满的,除非电脑内存不够了,这时会自动报错,因此返回值永远是0。
代码如下:
template<class Type>
bool linkStack<Type>::isFullStack()const
{
return 0;
}
void initializeStack()
链栈的初始化要分两种情况,第一种就是栈中有节点存在,这时就要依次释放节点的内存,然后把栈顶指针置为空。另外一种就是栈中无节点,由于构造函数已经把栈顶指针置为空,所以无需进行操作。不像数组栈初始化那么简单只需要把表示栈中元素个数的变量置为0就可以了。
代码如下:
template<class Type>
void linkStack<Type>::initializeStack()
{
while (stackTop)
{
node<Type>* temp = stackTop;
stackTop = stackTop->next;
delete temp;
}
}
void pop()
链栈弹出一个节点需要释放该节点的内存,然后让栈顶指针指向后一个节点就可以了,但是栈为空的情况要额外考虑,栈为空就在控制台输出提示信息。
代码如下:
template<class Type>
void linkStack<Type>::pop()
{
if (isEmptyStack())
{
cout << "The stack is empty,cannot pop" << endl;
}
else
{
node<Type>* temp;
temp = stackTop;
stackTop = stackTop->next;
delete temp;
}
}
void push(const Type&)
将元素压入栈中,这里得考虑一个点那就是我们到底是把一个节点作为push的输入还是把元素作为push的输入,对于使用者来说我才不管你这个数据结构内部是怎么写的,链栈也好,数组栈也罢其作为栈的功能都是一样的,只不过实现方式不同,那么对于使用者来说有的肯定是元素类型,如果还要自己建立一个node那不是很麻烦了,所以我们这里把元素直接作为输入。
因为压入栈中的是元素,所以我们得新建一个节点,然后把元素放入节点中,再让该节点变成栈的栈顶节点。链栈有个好处就是压栈不用考虑栈满的情况。
代码如下:
template<class Type>
void linkStack<Type>::push(const Type& info)
{
node<Type>* newNode = new node<Type>;
newNode->info = info;
newNode->next = stackTop;
stackTop = newNode;
}
Type top()const
同push函数,top函数的不是返回节点的地址而是直接返回元素,这与使用者的期望是符合的,top函数也需要做异常处理,因为该函数是有返回值的,所以一旦遇到空栈的情况我们就要终止程序,而不能仅仅在控制台上输出异常信息。
函数实现很简单,直接返回栈顶指针的元素就行了。
代码如下:
template<class Type>
Type linkStack<Type>::top()const
{
assert(!isEmptyStack());
return stackTop->info;
}
void copyStack(const linkStack&)
这个函数的功能就是实现链栈的拷贝,你也许会问这不是拷贝构造函数要解决的问题吗?实际上拷贝构造函数内会调用该函数,在这里触发拷贝构造函数只有两种情况,即链栈作为函数的输入或者输出参数时,而第三种情况的处理则丢给了=运算符重载。
拷贝过程首先要判断当前链栈是否为空,若非空则要将栈初始化。然后看被拷贝的栈是否为空,如果空则令当前栈的栈顶指针为空;否则进入拷贝过程,我们设置三个指针,其中两个用来推进拷贝过程,指向当前拷贝与被拷贝的节点,另外一个则用来生成新节点。我们不要立马进入循环,注意此时栈顶指针是空的,所以我们先要拷贝第一个节点,让栈顶指针指向该节点,然后就可以进入循环了不断拷贝了。
代码如下:
template<class Type>
void linkStack<Type>::copyStack(const linkStack<Type>& otherStack)
{
if (stackTop != nullptr)
{
initializeStack();
}
if(otherStack.isEmptyStack())
{
stackTop=nullptr;
}
else
{
stackTop = new node<Type>;
stackTop->info = otherStack.stackTop->info;
node<Type>* temp, * otherTemp;
temp = stackTop;
otherTemp = otherStack.stackTop->next;
while (otherTemp)
{
node<Type>* newNode = new node<Type>;
newNode->info = otherTemp->info;
temp->next = newNode;
temp = temp->next;
otherTemp = otherTemp->next;
}
temp->next = nullptr;
}
}
linkStack(const linkStack&)
拷贝构造函数,因为这里拷贝构造函数只在函数输入和输出的备份操作才会触发,所以是不会触发构造函数的,也就是说栈顶指针是个野指针,所以在拷贝构造函数中我们要把栈顶指针置为空,然后调用copyStack函数就行了。
代码如下:
template<class Type>
linkStack<Type>::linkStack(const linkStack<Type>& otherStack)
{
stackTop = nullptr;
copyStack(otherStack);
}
~linkStack()
一旦涉及到动态内存分配,析构函数就要释放内存,直接调用初始化函数即可。
代码如下:
template<class Type>
linkStack<Type>::~linkStack()
{
initializeStack();
}
const linkStack& operator=(const linkStack&)
重载=运算符需要注意的一点就是要避免自我赋值,然后直接调用copyStack函数即可
代码如下:
template<class Type>
const linkStack<Type>& linkStack<Type>::operator=(const linkStack<Type>& otherStack)
{
if (this != &otherStack) //避免自我复制
copyStack(otherStack);
return *this;
}
全部代码
stackADT.h
template <class Type>
class stackADT
{
public:
virtual void initializeStack() = 0;
virtual bool isEmptyStack()const = 0;
virtual bool isFullStack()const = 0;
virtual void push(const Type&) = 0;
virtual void pop() = 0;
virtual Type top()const = 0;
};
linkStack.h
#include<iostream>
#include "stackADT.h"
#include<string>
#include<cassert>
using namespace std;
template<class Type>
struct node
{
Type info;
node<Type>* next;
};
template<class Type>
class linkStack :public stackADT<Type>
{
public:
void initializeStack();
bool isEmptyStack()const;
bool isFullStack()const;
void push(const Type&);
void pop();
Type top()const;
linkStack();
linkStack(const linkStack<Type>&);
~linkStack();
const linkStack<Type>& operator=(const linkStack<Type>&);
private:
node<Type>* stackTop;
void copyStack(const linkStack<Type>&);
};
template<class Type>
linkStack<Type>::linkStack()
{
stackTop = nullptr;
}
template<class Type>
void linkStack<Type>::initializeStack()
{
while (stackTop)
{
node<Type>* temp = stackTop;
stackTop = stackTop->next;
delete temp;
}
}
template<class Type>
bool linkStack<Type>::isEmptyStack()const
{
return stackTop == nullptr;
}
template<class Type>
bool linkStack<Type>::isFullStack()const
{
return 0;
}
template<class Type>
void linkStack<Type>::push(const Type& info)
{
node<Type>* newNode = new node<Type>;
newNode->info = info;
newNode->next = stackTop;
stackTop = newNode;
}
template<class Type>
void linkStack<Type>::pop()
{
if (isEmptyStack())
{
cout << "The stack is empty,cannot pop" << endl;
}
else
{
node<Type>* temp;
temp = stackTop;
stackTop = stackTop->next;
delete temp;
}
}
template<class Type>
Type linkStack<Type>::top()const
{
assert(!isEmptyStack());
return stackTop->info;
}
template<class Type>
void linkStack<Type>::copyStack(const linkStack<Type>& otherStack)
{
if (stackTop != nullptr)
{
initializeStack();
}
if (otherStack.isEmptyStack())
{
stackTop = nullptr;
}
else
{
stackTop = new node<Type>;
stackTop->info = otherStack.stackTop->info;
node<Type>* temp, * otherTemp;
temp = stackTop;
otherTemp = otherStack.stackTop->next;
while (otherTemp)
{
node<Type>* newNode = new node<Type>;
newNode->info = otherTemp->info;
temp->next = newNode;
temp = temp->next;
otherTemp = otherTemp->next;
}
temp->next = nullptr;
}
}
template<class Type>
linkStack<Type>::linkStack(const linkStack<Type>& otherStack)
{
stackTop = nullptr;
copyStack(otherStack);
}
template<class Type>
const linkStack<Type>& linkStack<Type>::operator=(const linkStack<Type>& otherStack)
{
if (this != &otherStack)
copyStack(otherStack);
return *this;
}
template<class Type>
linkStack<Type>::~linkStack()
{
initializeStack();
}
主函数(测试)
#include "linkStack.h"
#include<iostream>
using namespace std;
void testCopyConstructor(linkStack<int> otherStack);
int main()
{
linkStack<int> stack;
linkStack<int> copyStack;
linkStack<int> dummyStack;
stack.initializeStack();
stack.push(23);
stack.push(45);
stack.push(38);
copyStack.push(11);
copyStack = stack; //copy stack into copyStack
cout << "The elements of copyStack: ";
while (!copyStack.isEmptyStack()) //print copyStack
{
cout << copyStack.top() << " ";
copyStack.pop();
}
cout << endl;
copyStack = stack;
testCopyConstructor(stack); //test the copy constructor
if (!stack.isEmptyStack())
cout << "The original stack is not empty." << endl
<< "The top element of the original stack: "
<< copyStack.top() << endl;
dummyStack = stack; //copy stack into dummyStack
cout << "The elements of dummyStack: ";
while (!dummyStack.isEmptyStack()) //print dummyStack
{
cout << dummyStack.top() << " ";
dummyStack.pop();
}
cout << endl;
return 0;
}
void testCopyConstructor(linkStack<int> otherStack)
{
if (!otherStack.isEmptyStack())
cout << "otherStack is not empty." << endl
<< "The top element of otherStack: "
<< otherStack.top() << endl;
}
[[栈的数组实现]]