Я написал свой собственный класс списка для практики, вот он:
#pragma once
#include <iostream>
using namespace std;
// A linked node structure.
template <typename T>
struct LinkedNode
{
T data;
LinkedNode<T>* next;
LinkedNode<T>* prev;
};
// Class linked list.
template <typename T>
class LinkedList
{
public:
// Constructor
LinkedList()
{
mFirst = 0;
mLast = 0;
}
LinkedList(const LinkedList& rhs)
{
mFirst = 0;
mLast = 0;
// Get a pointer to the first node of rhs.
LinkedNode<T>* current = rhs.mFirst;
// While not at the end of the rhs linked list.
while( current != 0 )
{
insertLast( current->data );
current = current->next;
}
}
// Destructor
~LinkedList()
{
destroy();
}
// Overload
LinkedList& operator=(const LinkedList& rhs)
{
// Check for self assignment
if ( this == &rhs )
return *this;
// Get a pointer to the first node of rhs.
LinkedNode<T>* current = rhs.mFirst;
// While not at the end of the rhs linked list.
while( current != 0 )
{
insertLast( current->data );
current = current->next;
}
// Chain assignments a = b = c = d
return *this;
}
// Check if list is empty.
bool isEmpty()
{
if( mFirst == 0 && mLast == 0 )
return true;
else
return false;
}
// Return first and last nodes.
LinkedNode<T>* getFirst() { return mFirst; };
LinkedNode<T>* getLast() { return mLast; };
void insertFirst(T tData);
void insertLast(T tData);
bool insertAfter(T tKey, T tData);
void removeFirst();
void removeLast();
void remove(T removalCandidate);
void destroy();
private:
LinkedNode<T>* mFirst;
LinkedNode<T>* mLast;
};
template <typename T>
bool LinkedList<T>::insertAfter(T tKey, T tData)
{
if( isEmpty() ) return false;
// Get a pointer to the front of the list
LinkedNode<T>* current = mFirst;
// Loop until we find the tKey (the value of the node to insert after)
while( current->data != tKey )
{
// Hop to the next node.
current = current->next;
// Test if we reached the end, if we did we didn't find the node to insert after (tKey)
if( current == 0 )
return false;
}
// Allocate memory for the new node to insert.
LinkedNode<T>* newNode = new LinkedNode<T>();
newNode->data = tData;
// Special case: Are we inserting after the last node?
if( current == mLast )
{
newNode->next = 0;
mLast = newNode;
}
// No, else link in new node after the current node.
else
{
newNode->next = current->next;
newNode->next->prev = newNode;
}
newNode->prev = current;
current->next = newNode;
return true;
}
template <typename T>
void LinkedList<T>::insertFirst(T tData)
{
LinkedNode<T>* newNode = new LinkedNode<T>();
newNode->data = tData;
// If the list is empty, then this is the first and last node and doesn't have a previous pointer.
if( isEmpty() )
mLast = newNode;
// If the list is not empty, the new node becomes the previous node of the current first node.
else
mFirst->prev = newNode;
// The new node's next pointer is the old first pointer. May be null if this is the first node being added to the list.
newNode->next = mFirst;
//The new node becomes the first node.
mFirst = newNode;
}
template <typename T>
void LinkedList<T>::insertLast(T tData)
{
LinkedNode<T>* newNode = new LinkedNode<T>();
newNode->data = tData;
if( isEmpty() )
mFirst = newNode;
else
mLast->next = newNode;
newNode->prev = mLast;
mLast = newNode;
}
template <typename T>
void LinkedList<T>::removeFirst()
{
if( !isEmpty() )
{
LinkedNode<T>* current = mFirst;
if( mFirst == mLast )
{
mFirst->next = 0;
mLast->prev = 0;
}
else
{
mFirst = current->next;
mFirst->prev = 0;
}
delete current;
current = 0;
}
}
template <typename T>
void LinkedList<T>::removeLast()
{
if( !isEmpty() )
{
LinkedNode<T>* current = mLast;
if( mFirst == mLast )
{
mFirst = 0;
mLast = 0;
}
else
{
mLast = current->prev;
mLast->next = 0;
}
delete current;
current = 0;
}
}
template <typename T>
void LinkedList<T>::remove(T removalCandidate)
{
LinkedNode<T>* current = mFirst;
if( isEmpty() )
{
cout << "List is empty!" << endl;
}
else
{
while( current->data != removalCandidate )
{
if( current->data == removalCandidate )
{
break;
}
current = current->next;
}
}
if( current == mFirst )
removeFirst();
else if ( current == mLast )
removeLast();
else
{
// Create two linked nodes to access the next and prev nodes.
LinkedNode<T>* left = current->prev;
LinkedNode<T>* right = current->next;
left->next = right;
right->prev = left;
}
delete current;
current = 0;
}
template <typename T>
void LinkedList<T>::destroy()
{
// Is there at least one node in the list?
if( mFirst != 0 )
{
// Get a pointer to the first node.
LinkedNode<T>* current = mFirst;
// Loop until we reach the end of list.
while( current != 0 )
{
// Save the current node.
LinkedNode<T>* oldNode = current;
// Move to next node.
current = current->next;
// Delete saved node.
delete oldNode;
oldNode = 0;
}
}
mFirst = 0;
}
Затем я также написал классы стека и очереди, и я протестировал их оба, чтобы определить, является ли строка палиндромом. Это все работает. Однако, когда он запускает метод destroy в классе списка, он выдает ошибку: block type valid (pHead-> nBlockUse) на
delete oldNode;
линия.
Код очереди:
#pragma once
#include <iostream>
#include "LinkedList.h"
#include <cassert>
template <typename T>
class Queue
{
public:
Queue(){};
~Queue()
{
mList.destroy();
}
T& getFirst()
{
assert( !isEmpty() );
return mList.getFirst()->data;
}
bool isEmpty( void )
{
return mList.isEmpty();
}
void push( T newElement )
{
mList.insertLast( newElement );
}
void pop()
{
mList.removeFirst();
}
private:
LinkedList<T> mList;
};
Код стека:
#pragma once
#include <iostream>
#include "LinkedList.h"
#include <cassert>
template <typename T>
class Stack
{
public:
Stack(){};
~Stack()
{
mList.destroy();
}
T& getTopItem()
{
// Throw error if list is empty.
assert( !isEmpty() );
return mList.getLast()->data;
}
bool isEmpty( void )
{
return mList.isEmpty();
}
void push( T newElement )
{
mList.insertLast( newElement );
}
void pop()
{
mList.removeLast();
}
private:
LinkedList<T> mList;
};
Основной код:
#include "Stack.h"#include "Queue.h"#include <iostream>
#include <string>
using namespace std;
int main()
{
Stack<char> strStack;
Queue<char> strQueue;
cout << "Enter a string: ";
string input = "";
getline(cin, input);
for( unsigned int i = 0; i < input.length(); ++i )
{
strStack.push( input[i] );
strQueue.push( input[i] );
}
bool isPalindrome = true;
// FIX PALINDROME AND ASSERTION FAIL
for( unsigned int j = 0; j < input.length(); ++j )
{
if( strStack.getTopItem() != strQueue.getFirst() )
{
isPalindrome = false;
break; // Exit for loop early.
}
// Get rid of next element out.
strStack.pop();
strQueue.pop();
}
cout << input << " is palindrome: " << isPalindrome << endl;
}
Я не уверен, почему, любая помощь будет оценена.
Проблема в removeFirst
функция: когда вы удаляете последний узел в списке (т.е. mFirst == mLast
является true
) вы устанавливаете указатели ссылки на узел на 0
, а затем вы удалите узел. Теперь оба mFirst
а также mLast
указывает на удаленный узел!
Вместо установки next
а также prev
ссылки на 0
, вы должны установить mFirst
а также mLast
в 0
, Так же, как вы уже делаете в removeLast
,
Потому что вы не устанавливаете mFirst в NULL (0) непосредственно перед возвращением метода destroy (). Если вы вызываете destroy () более одного раза, вы получите такое утверждение crt в результате попытки дважды удалить указатель. Класс очереди вызывает mList.destroy в своем деструкторе, но деструктор mList снова вызовет destroy.