c++栈实现(家庭作业)

C++ stack implementation (homework) PT. II

本文关键字:家庭作业 实现 c++      更新时间:2023-10-16

进展顺利。我现在的问题是关于拷贝构造和深度拷贝。我的堆栈目前一直在崩溃,我有一种感觉,它正在这样做,因为它所依赖的类不允许复制构造和操作符重载。但无论如何,这是我的代码(我为代码量道歉,但它可能是必要的,以了解我失败的地方):

依赖类,链表

#ifndef linkList_H
#define linkList_H

//
// Create an object to represent a Node in the linked list object
// (For now, the objects to be put in the list will be integers)
//
struct Node
{
    int   number;
    Node* next;
    Node* prev;
    // needs copy constructor?
    // needs overloaded assignment operators for copying?
    // needs overloaded operators for incrementing and decrementing?
};

//
// Create an object to keep track of all parts in the list
//
class List
{
public:
    //
    // Contstructor intializes all member data
    //
    List() : m_size(0), m_listHead(0) {}
    //
    // methods to return size of list and list head
    //
    Node*    getListHead() const { return m_listHead; }
    unsigned getListSize() const { return m_size; }
    //
    // methods for adding and inserting a new node to the linked list, 
    // retrieving and deleting a specified node in the list
    //
    void  addNode(int num);
    void  deleteNode(Node* current);
    void  insertHead(Node* current);
    void  insertAfter(Node* current, int newValue);
    void  insertBefore(Node* current, int newValue);
    Node* retrieveNode(unsigned position);
private:
    //
    // member data consists of an unsigned integer representing
    // the list size and a pointer to a Node object representing head
    //
    Node*    m_listHead;
    unsigned m_size;
};
#endif

linkedList的实现

#include "linkList.h"
#include <iostream>
using namespace std;

//
// Adds a new node to the linked list
//
void List::addNode(int num)
{
    Node *newNode = new Node;
    newNode->number = num;
    newNode->next = m_listHead;
    if( m_listHead )
        m_listHead->prev = newNode;
    m_listHead = newNode;
    ++m_size;
}

//
// Inserts a node which has already been set to front
// of the list
//
void List::insertHead(Node* current)
{
    int value = current->number;
    deleteNode(current);
    addNode(value);
}

//
// Inserts a node which has already been set before a
// specified location in the list
//
void List::insertBefore(Node* current, int newValue)
{
    current = current->prev;
    current->number = newValue;
}

//
// Inserts a node which has already been set before a
// specified location in the list
//
void List::insertAfter(Node* current, int newValue)
{
    current = current->next;
    current->number = newValue;
}

//
// Deletes a node from a specified position in linked list
//
void List::deleteNode(Node* current)
{
    --m_size;
    if(current == m_listHead) 
        m_listHead = current->next;
    if(current->prev) 
        current->prev->next = current->next;
    if(current->next) 
       current->next->prev = current->prev;
}

//
// Retrieves a specified node from the list
//
Node* List::retrieveNode(unsigned position)
{
    if(position > (m_size-1) || position < 0)
    {
        cout << "Can't access node; out of list bounds";
        cout << endl;
        cout << endl;
        exit(EXIT_FAILURE);
    }
    Node* current = m_listHead;
    unsigned pos = 0;
    while(current != 0 && pos != position)
    {
        current = current->next;
        ++pos;
    }
    return current;
}

下面是堆栈类:

#ifndef stack_H
#define stack_H
#include "linkList.h"

class Stack
{
public:
    //
    // Constructor, copy constructor and destructor to initialize stack 
    // data, copy data and clear up memory, respectively
    //
   Stack() : m_top(0), m_stack(new List()) {}
   Stack(const Stack &rhs);
   ~Stack() { delete [] m_stack; }
    //
    // functionality to determine if stack is empty
    //
    bool isEmpty();
    //
    // methods for pushing data on to stack and for
    // popping data from the stack
    //
    void push(int newValue);
    int  pop();
    //
    // accessor functions for retrieving the value on top of stack
    // and for returning the stack size
    //
    int getTop() const { return m_top->number; }
    int getSize() const { return m_stack->getListSize(); }
    //
    // overloaded assignment operator for copying stack
    //
    Stack& operator=(const Stack &rhs);
private:
    //
    // member data which represent the stack, the top
    // of the stack and the size of the stack
    //
    Node* m_top;
    List* m_stack;
};
#endif

最后,这里是堆栈实现

#include "stack.h"
#include <iostream>
using namespace std;
//
// Copy constructor
//
Stack::Stack(const Stack &rhs)
    : m_top(rhs.m_top), m_stack(rhs.m_stack)
{
}
//
// if the Top of stack is zero, return true
//
bool Stack::isEmpty()
{
    if( m_top == 0 ) return true;
    else return false;
}
//
// increment stack pointer, place new value in stack
//
void Stack::push(int newValue)
{
    ++m_top;               
    m_top->number = newValue;   // crashes on this statement
}
//
// if the stack is empty, throw an error message
//
int Stack::pop()
{
    if( isEmpty() )
    {
        cout << "Error: stack underflow" << endl;
        exit(EXIT_FAILURE);
    }
    --m_top;
    return (m_top + 1)->number;
}

Stack& Stack::operator=(const Stack &rhs)
{
    if( this != &rhs )
        delete [] m_stack;
    m_stack = new List();
    m_stack = rhs.m_stack;
    m_top = rhs.m_top;
   return *this;
}

程序在主程序中执行以下简单代码后崩溃:

Stack stack;
stack.push(1);

再次为这里的代码量感到抱歉。我相信我这里的问题是,节点对象需要重载操作符,以便"增量"或"减量"创建/删除节点时,一个值被推/弹出到堆栈。是这样吗?另外,我不确定是否需要Node对象的复制构造函数。这里可能有更多的问题(也许算法是不正确的?也许复制构造是不正确的堆栈?)。什么好主意吗?

让我们逐行分析崩溃场景。(很好,因为它只有两行!)
首先是语句:Stack stack;
这将调用堆栈构造函数,它将把m_top设置为什么值?

下一行是:stack.push(1);
stack.push的两个语句都将使用m_top
第一个语句是++m_top;
给定m_top开始的值,它现在的值是多少?

第二个语句是m_top->number = newValue;
如果您正确地回答了前面关于m_top的问题,那么此时程序崩溃的原因就应该很清楚了。如何修复它也应该相对清楚。

m_top在构造函数中设置为0。但是…

你的Stack只需要以一种非常具体的方式装饰你的List。从简短地看你的List头,这一切都应该是相对容易实现的,如果你了解堆栈是如何工作的…

你的Stack不需要保持"顶部"。它应该根据您的List来实现。push到堆栈应该在列表的前面添加一个新节点,而弹出堆栈应该从列表的前面删除一个节点。

首先,你不能在m_stack (delete [] m_stack)上使用数组删除操作符,因为那里只有一个对象而不是数组(m_stack = new List)。这将导致崩溃。(实际上我不明白为什么它是动态创建的。)

然后,您需要为List类编写适当的赋值操作符和复制构造函数,它们将复制节点,还需要编写析构函数来清理分配的节点,但这与崩溃无关(但将来很容易导致崩溃)。

当前的崩溃原因,就像其他人说的,Stack试图访问m_top,而它是空的。

但是主要的问题是糟糕的设计,它使你以一种不明确的方式使用List类:/
  • 首先,如果你有一个双向链表,list类也应该跟踪尾部指针而不仅仅是头部指针。这会让你从很多头痛中解脱出来。
  • 那么,addHead, addTail, insertBefore, insertAfter不应该依赖于用户创建节点,而是在函数中创建节点,并返回新创建的节点。
  • 此外,nextprev指针暴露在外,并且可以在List类之外更改,这是非常不安全的。我建议你将prev和next设置为私有,并为它们公开一个getter,这样它们就不能被更改(在这种情况下,List类需要是Node结构/类的朋友)。

我认为,这些将给你一个很好的开始,如何从头开始重写列表和堆栈。另外,请注意当前未处理的节点的所有权。

在构造函数中将m_top初始化为0 (null),然后尝试在push()中使用它

最好的方法是一次测试这些类中的一个。因为Stack依赖于List,所以先调试List。我假设您可以访问调试器。如果没有,就去买一个,学着怎么用。您还需要为List类编写一些测试代码。

好运。