2016-10-16 98 views
-2

我一直在寻找相当多的东西,我一直无法找到解决方案。对于我的数据结构课程,我被要求使用链表创建一个堆栈。在大多数情况下,它运作良好,我的教授甚至给了我24/25的任务,但它一直困扰着我,它仍然崩溃。我怀疑问题在于复制构造函数或弹出操作。这里是头文件:使用链接列表的C++堆栈双删除问题

/******************************************************************************* 
*** DESCRIPTION : This file implements a stack ADT using a linked list. *** 
***     Besides the standard constructor, copy, and destructor *** 
***     functions, the available functions include:    *** 
***      * push - pushes data onto the stack     *** 
***      * pop - pops data from the stack      *** 
***      * peek - view the top element on the stack   *** 
***      * view - output every element on the stack   *** 
*******************************************************************************/ 

#ifndef _KIELASJC2_H 
#define _KIELASJC2_H 

// User specified element type 
typedef char ElementType; 

class StackType{ 
    // Exportable definitions 
    public: 
     // Constructor 
     StackType(); 
     // Copy constructor 
     StackType(StackType& copyStack); 
     // Destructor 
     ~StackType(); 
     // Push data onto the stack 
     void push(const ElementType pushElem); 
     // Pop data from the stack 
     void pop(ElementType& popElem); 
     // Peek the top element on the stack without changing the stack 
     void peek(ElementType& peekElem); 
     // Look at every element in the stack without changing the stack 
     void view(); 

    private: 
    // Non-exportable definitions 
     struct NodeType; 
     typedef NodeType* PointerType; 
     struct NodeType{ 
      ElementType element; 
      PointerType next; 
     }; 
     PointerType theTop; 
     bool isEmpty(PointerType tempNode); 
     bool isFull(PointerType tempNode); 
}; 

#endif 

,这里是实现文件:

/******************************************************************************* 
*** DESCRIPTION : This file implements a stack ADT using a linked list. *** 
***     Besides the standard constructor, copy, and destructor *** 
***     functions, the available functions include:    *** 
***      * push - pushes data onto the stack     *** 
***      * pop - pops data from the stack      *** 
***      * peek - view the top element on the stack   *** 
***      * view - output every element on the stack   *** 
***     It also includes two private functions:     *** 
***      * isEmpty - checks to see if the stack is empty  *** 
***      * isFull - checks to make sure temp wasn't NULL  *** 
*******************************************************************************/ 

#include <iostream> // For using cout 
#include <iomanip> // For manipulating output 
#include "kielasjc2.h" // Header to this implementation file 

// Debug 
#define DEBUG 0 

// Namespace 
using namespace std; 

/******************************************************************************* 
*** FUNCTION Constructor             *** 
******************************************************************************** 
*** DESCRIPTION : Initializes the class by setting theTop and tempNode to *** 
***     NULL.             *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : NONE              *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : NONE              *** 
*******************************************************************************/ 
StackType::StackType() { 
    // Initialize both top and temp to NULL 
    theTop = NULL; 

} 

/******************************************************************************* 
*** FUNCTION Copy Constructor            *** 
******************************************************************************** 
*** DESCRIPTION : Creates a copy of the stack linked list by popping all *** 
***     elements in the first stack into a temporary stack. All *** 
***     elements from the temporary stack are then pushed back *** 
***     into the copied stack and original stack.    *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : StackType& copyStack - copy of the original stack  *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : NONE              *** 
*******************************************************************************/ 
StackType::StackType(StackType& copyStack) { 
    // Point the top to NULL 
    theTop = NULL; 
    // Create a temporary stack 
    StackType tempStack; 
    // Create a temporary element 
    ElementType tempElem; 
    // Create a temporary pointer to the top 
    //PointerType tempNode = theTop; 
    // Create a temporary pointer to the the top of the copy stack 
    //PointerType tempTempNode = tempStack.theTop; 
    //PointerType copyTempNode = copyStack.theTop; 

    // Loop through the entire stack and pop each element into the temp stack 
    while (!copyStack.isEmpty(copyStack.theTop)) { 
     // Pop the current stack 
     copyStack.pop(tempElem); 
     // Push the popped element into the temp stack 
     tempStack.push(tempElem); 
    } 

    // Refill the current and copy stacks with the temp stack 
    while (!tempStack.isEmpty(tempStack.theTop)){ 
     // Pop the temp stack 
     tempStack.pop(tempElem); 
     // Push the popped element onto the current newly copied stack 
     push(tempElem); 
     // Push the same popped element back onto the copy stack 
     copyStack.push(tempElem); 
    } 

} 

/******************************************************************************* 
*** FUNCTION Destructor              *** 
******************************************************************************** 
*** DESCRIPTION : Destroys the current stack by popping every element.  *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : NONE              *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : NONE              *** 
*******************************************************************************/ 
StackType::~StackType() { 
    // Create a temporary element just so we can call pop 
    ElementType tempElem; 

    #if DEBUG 
     cout << "[+] DEBUG: FUNCTION - Destructor" << endl; 
     cout << "[+]  theTop: " << theTop << endl; 
    #endif 

    // While the stack is not empty, pop 
    while (!isEmpty(theTop)) { 
     // Pop the top node 
     pop(tempElem); 
    } 

} 

/******************************************************************************* 
*** FUNCTION push               *** 
******************************************************************************** 
*** DESCRIPTION : Pushes the input element onto the current stack.   *** 
*** INPUT ARGS : const ElementType pushElem - element to be pushed onto *** 
***      the current stack.         *** 
*** OUTPUT ARGS : NONE              *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : NONE              *** 
*******************************************************************************/ 
void StackType::push(const ElementType pushElem) { 
    // Create a pointer to a new node 
    PointerType tempNode = new (nothrow) NodeType; 

    // Verify that the node isn't full 
    if (isFull(tempNode)) { 
     // Tell the user if new memory couldn't be allocated 
     cout << "[!] ERROR: (push) New dynamic node could not be created."; 
     cout << endl; 
     return; 
    } 

    #if DEBUG 
     ElementType tempElem; 
     cout << "[+] DEBUG: FUNCTION - push" << endl; 
     peek(tempElem); 
     cout << "[+]  top before the push: " << tempElem << endl; 
     cout << "[+]  Top ptr before: " << theTop << endl; 
    #endif 

    // Set the temporary node element to the element being pushed 
    tempNode->element = pushElem; 
    // Set the next pointer to the top 
    tempNode->next = theTop; 

    // If these two lines were flipped, the code works, but it creates a memory 
    // leak, so that doesnt work either 
    theTop = tempNode; 
    tempNode = NULL; 

    #if DEBUG 
     peek(tempElem); 
     cout << "[+]  Top ptr after: " << theTop << endl; 
     cout << "[+]  top after the push: " << tempElem << endl; 
    #endif 

} 

/******************************************************************************* 
*** FUNCTION pop               *** 
******************************************************************************** 
*** DESCRIPTION : Pops the top element off of the current stack.   *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : ElementType& popElem - Stores the element that was  *** 
***      popped from the stack. 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : NONE              *** 
*******************************************************************************/ 
void StackType::pop(ElementType& popElem) { 
    // Create a temporary pointer 
    PointerType tempNode = theTop; 

    // Point temp to the same node the top is pointing to 
    //tempNode = theTop; 

    // Make sure the stack isn't empty 
    if (isEmpty(tempNode)) { 
     // Tell the user if the stack is empty 
     cout << "[!] ERROR: (pop) No node available to pop, stack is empty."; 
     cout << endl; 
     return; 
    } 

    #if DEBUG 
     cout << "[+] DEBUG: FUNCTION - pop" << endl; 
     cout << "[+]  Top ptr before: " << theTop << endl; 
    #endif 

    // Point the top to the next node 
    theTop = theTop->next; 
    // Derefrence the next node of the current node being popped 
    tempNode->next = NULL; 
    // Pop the element into popElem 
    popElem = tempNode->element; 
    // Delete the node 
    delete tempNode; 
    // Point temp back to NULL 
    tempNode = NULL; 

    #if DEBUG 
     cout << "[+]  Top ptr after: " << theTop << endl; 
     cout << "[+]  Element being popped: " << popElem << endl; 
    #endif 

} 

/******************************************************************************* 
*** FUNCTION peek               *** 
******************************************************************************** 
*** DESCRIPTION : Looks at the top element of the current stack by popping *** 
***     it. The element is then pushed back onto the top of the *** 
***     current stack.           *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : ElementType& peekElem - stores the element being peeked. *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : NONE              *** 
*******************************************************************************/ 
void StackType::peek(ElementType& peekElem) { 
    // Create a temporary pointer 
    //PointerType tempNode; 

    // Point temp to the top 
    //tempNode = theTop; 

    // Make sure the stack isn't empty 
    if (isEmpty(theTop)) { 
     // Tell the user if the stack is empty 
     cout << "[!] ERROR: (peek) Stack is empty, no element to peek at."; 
     cout << endl; 
     // Return a null value if the stack is empty 
     peekElem = '\0'; 
     return; 
    } 

    // Pop the top element into peekElem 
    pop(peekElem); 

    // Push the popped element back onto the top of the stack 
    push(peekElem); 

    #if DEBUG 
     cout << "[+] DEBUG: FUNCTION - peek" << endl; 
     cout << "[+]  Peeked value: " << peekElem << endl; 
    #endif 

} 

/******************************************************************************* 
*** FUNCTION view               *** 
******************************************************************************** 
*** DESCRIPTION : Shows the user every element on the current stack by  *** 
***     popping every element and pushing them into a temporary *** 
***     stack. Once the user has seen the contents of the stack, *** 
***     the elements on the temporary stack are popped and  *** 
***     pushed back onto the current stack.      *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : NONE              *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : NONE              *** 
*******************************************************************************/ 
void StackType::view() { 
    // Create a temporary stack 
    StackType tempStack; 
    // Create a temporary element 
    ElementType tempElem; 
    // Point a temporary node to the top 
    //PointerType tempNode = theTop; 
    // Point a temporary node to the top of the copy stack 
    //PointerType copyTempNode; 

    // View output for the user 
    cout << "The Top -> "; 

    // Loop through the entire stack and pop each element into the temp stack 
    while (!isEmpty(theTop)) { 
     // Pop the current stack 
     pop(tempElem); 
     // Push the popped element into the temp stack 
     tempStack.push(tempElem); 
     // Output the elements as the stack is popped 
     cout << tempElem << " -> "; 
    } 

    cout << "The Bottom" << endl; 

    // Refill the current stack with the temp stack 
    while (!tempStack.isEmpty(tempStack.theTop)){ 
     // Pop the temp stack 
     tempStack.pop(tempElem); 
     // Push the popped element onto the current stack 
     push(tempElem); 
    } 

} 

/******************************************************************************* 
*** FUNCTION isEmpty              *** 
******************************************************************************** 
*** DESCRIPTION : Checks to see whether or not the stack is empty.   *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : NONE              *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : bool true/false - true if the stack is empty, false  *** 
***      if the stack is not empty.       *** 
*******************************************************************************/ 
bool StackType::isEmpty(PointerType tempNode) { 
    // If the top isn't pointing to NULL, our stack isn't empty yet 
    if (tempNode != NULL) { 
     return false; 
    } else { 
     return true; 
    } 

} 

/******************************************************************************* 
*** FUNCTION isFull               *** 
******************************************************************************** 
*** DESCRIPTION : Checks to see if the current stack is full after   *** 
***     attempting to allocate new memory for the next node.  *** 
***     This will only provide a relevent value if it is used *** 
***     immediately after trying to allocate memory for a new *** 
***     node being pointed to by the tempNode.     *** 
*** INPUT ARGS : NONE              *** 
*** OUTPUT ARGS : NONE              *** 
*** IN/OUT ARGS : NONE              *** 
*** RETURN  : bool true/false - true if the tempNode is NULL, false *** 
***      if a pointer value was assigned to tempNode.   *** 
*******************************************************************************/ 
bool StackType::isFull(PointerType tempNode) { 
    // If temp isn't NULL, we have successfully allocated memory. 
    // This function should only be used immediately after allocating new 
    // memory to the tempNode. 
    if (tempNode != NULL) { 
     return false; 
    } else { 
     return true; 
    } 

} 

这是我的测试代码:

#include <iostream> 
#include "kielasjc2.h" 

using namespace std; 

int main(int argc, char* argv[]) { 
    StackType stack; 
    StackType stack2; 

    stack.push('a'); 

    stack2 = stack; 

    cout << "Stack: "; 
    stack.view(); 
    cout << endl; 

    cout << "Stack2: "; 
    stack2.view(); 
    cout << endl; 

} 

最后,这是输出(包括错误)我收到:

Stack: The Top -> a -> The Bottom 

Stack2: The Top -> a -> The Bottom 

*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0000000001d78010 *** 
======= Backtrace: ========= 
/lib64/libc.so.6(+0x75f3e)[0x7f9b25fb0f3e] 
/lib64/libc.so.6(+0x78d8d)[0x7f9b25fb3d8d] 
./a.out[0x400c67] 
./a.out[0x400b35] 
./a.out[0x400f50] 
/lib64/libc.so.6(__libc_start_main+0xfd)[0x7f9b25f59d1d] 
./a.out[0x400919] 
======= Memory map: ======== 
00400000-00402000 r-xp 00000000 00:15 9699818       /users/csc300/kielasjc/CSC300/A2_Stack_ADT/a.out 
00601000-00602000 rw-p 00001000 00:15 9699818       /users/csc300/kielasjc/CSC300/A2_Stack_ADT/a.out 
01d78000-01d99000 rw-p 00000000 00:00 0         [heap] 
7f9b20000000-7f9b20021000 rw-p 00000000 00:00 0 
7f9b20021000-7f9b24000000 ---p 00000000 00:00 0 
7f9b25f3b000-7f9b260c5000 r-xp 00000000 fd:00 917513      /lib64/libc-2.12.so 
7f9b260c5000-7f9b262c5000 ---p 0018a000 fd:00 917513      /lib64/libc-2.12.so 
7f9b262c5000-7f9b262c9000 r--p 0018a000 fd:00 917513      /lib64/libc-2.12.so 
7f9b262c9000-7f9b262cb000 rw-p 0018e000 fd:00 917513      /lib64/libc-2.12.so 
7f9b262cb000-7f9b262cf000 rw-p 00000000 00:00 0 
7f9b262cf000-7f9b262e5000 r-xp 00000000 fd:00 917506      /lib64/libgcc_s-4.4.7-20120601.so.1 
7f9b262e5000-7f9b264e4000 ---p 00016000 fd:00 917506      /lib64/libgcc_s-4.4.7-20120601.so.1 
7f9b264e4000-7f9b264e5000 rw-p 00015000 fd:00 917506      /lib64/libgcc_s-4.4.7-20120601.so.1 
7f9b264e5000-7f9b26568000 r-xp 00000000 fd:00 917544      /lib64/libm-2.12.so 
7f9b26568000-7f9b26767000 ---p 00083000 fd:00 917544      /lib64/libm-2.12.so 
7f9b26767000-7f9b26768000 r--p 00082000 fd:00 917544      /lib64/libm-2.12.so 
7f9b26768000-7f9b26769000 rw-p 00083000 fd:00 917544      /lib64/libm-2.12.so 
7f9b26769000-7f9b26851000 r-xp 00000000 fd:00 1049574     /usr/lib64/libstdc++.so.6.0.13 
7f9b26851000-7f9b26a51000 ---p 000e8000 fd:00 1049574     /usr/lib64/libstdc++.so.6.0.13 
7f9b26a51000-7f9b26a58000 r--p 000e8000 fd:00 1049574     /usr/lib64/libstdc++.so.6.0.13 
7f9b26a58000-7f9b26a5a000 rw-p 000ef000 fd:00 1049574     /usr/lib64/libstdc++.so.6.0.13 
7f9b26a5a000-7f9b26a6f000 rw-p 00000000 00:00 0 
7f9b26a6f000-7f9b26a8f000 r-xp 00000000 fd:00 917528      /lib64/ld-2.12.so 
7f9b26c7d000-7f9b26c82000 rw-p 00000000 00:00 0 
7f9b26c8b000-7f9b26c8e000 rw-p 00000000 00:00 0 
7f9b26c8e000-7f9b26c8f000 r--p 0001f000 fd:00 917528      /lib64/ld-2.12.so 
7f9b26c8f000-7f9b26c90000 rw-p 00020000 fd:00 917528      /lib64/ld-2.12.so 
7f9b26c90000-7f9b26c91000 rw-p 00000000 00:00 0 
7ffe362e8000-7ffe362fd000 rw-p 00000000 00:00 0       [stack] 
7ffe36359000-7ffe3635a000 r-xp 00000000 00:00 0       [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0     [vsyscall] 
Aborted 
+1

当你使用调试器来逐步执行代码,每次一行,并在调试器中做出什么看法? –

+0

我意识到问题发生在析构函数被调用时。我结束了多次删除同一个节点,从而导致双重空闲错误。 – magicbycalvin

回答