1

So I have a linked list of a class, and I am trying to delete one of the nodes, but when it reaches that line of code, the program freezes and crashes.

Here is the linked list classes I am using:

template <class Type>
struct nodeType {
    Type info;
    nodeType<Type>* link;
};

template <class Type>
class linkedListIterator {
public:
    linkedListIterator();
    linkedListIterator(nodeType<Type>* ptr);

    Type operator*();
    linkedListIterator<Type> operator++();
    bool operator==(const linkedListIterator<Type>& right) const;
    bool operator!=(const linkedListIterator<Type>& right) const;
private:
    nodeType<Type>* current;
};

template <class Type>
class linkedListType {
public:
    const linkedListType<Type>& operator=(const linkedListType<Type>&);

    void initializeList();
    bool isEmptyList() const;
    void print() const;
    int length() const;
    void destroyList();

    Type front() const;
    Type back() const;

    virtual bool search(const Type& searchItem) const = 0;
    virtual void insertFirst(const Type& newItem) = 0;
    virtual void insertLast(const Type& newItem) = 0;
    virtual void deleteNode(const Type& deleteItem) = 0;

    linkedListIterator<Type> begin();
    linkedListIterator<Type> end();

    linkedListType();
    linkedListType(const linkedListType<Type>& otherList);
    ~linkedListType();
protected:
    int count;
    nodeType<Type>* first; 
    nodeType<Type>* last; 
private:
    void copyList(const linkedListType<Type>& otherList);
};

template <class Type>
class orderedLinkedList : public linkedListType<Type> {
    using linkedListType<Type>::first;
    using linkedListType<Type>::last;
    using linkedListType<Type>::count;
public:
    bool search(const Type& searchItem) const;
    void insert(const Type& newItem);
    void insertFirst(const Type& newItem);
    void insertLast(const Type& newItem);
    void deleteNode(const Type& deleteItem);
};

This is the function I am calling to delete the node:

template <class Type>
void orderedLinkedList<Type>::deleteNode(const Type& deleteItem)
{
    nodeType<Type>* current; //pointer to traverse the list
    nodeType<Type>* trailCurrent; //pointer just before current
    bool found;
    if (first == nullptr) //Case 1
        cout << "Cannot delete from an empty list." << endl;
    else
    {
        current = first;
        found = false;
        while (current != nullptr && !found) //search the list
            if (current->info >= deleteItem)
                found = true;
            else
            {
                trailCurrent = current;
                current = current->link;
            }
        if (current == nullptr) //Case 4
            cout << "The item to be deleted is not in the "
            << "list." << endl;
        else
            if (current->info == deleteItem) //the item to be
            //deleted is in the list
            {
                if (first == current) //Case 2
                {
                    first = first->link;
                    if (first == nullptr)
                        last = nullptr;
                    delete current;
                }
                else //Case 3
                {
                    trailCurrent->link = current->link;
                    if (current == last)
                        last = trailCurrent;
                    delete current;
                }
                count--;
            }
            else //Case 4
                cout << "The item to be deleted is not in the "
                << "list." << endl;
    }
}//end deleteNode

And here is where I call the deleteNode function:

void deleteBook(orderedLinkedList<bookType> books)
{
    char choice = '\0';
    char ans = '\0';
    string title = "Delete Book";
    int len = title.length();
    int bookCount = bookType::getBookCount();

    do {
        system("cls");
        cout << setfill(' ');
        cout << setw((SIZE_MENUS / 2) + LEN_TITLE / 2) << right << TITLE << endl;
        cout << setw((SIZE_MENUS / 2) + len / 2) << right << title << endl;
        cout << endl;

        cout << setw(SIZE_MENUS / 3) << "" << "Current Book Count: " << bookCount << endl;
        cout << endl;

        linkedListIterator<bookType> current = lookUpBook(books);

        if (current != nullptr)
        {
            cout << setfill(' ');
            cout << setw(SIZE_MENUS / 4) << "" << "Is this the book you would like to delete? (Y/N): ";
            cin >> ans;
            cin.ignore(100, '\n');
            cout << endl;
            while (ans != 'Y' && ans != 'y' && ans != 'N' && ans != 'n')
            {
                cout << setw(SIZE_MENUS / 4) << "" << "Please enter Y or N." << endl;
                cout << setw(SIZE_MENUS / 4) << "" << "Is this the book you would like to delete? (Y/N): ";
                cin >> ans;
                cin.ignore(100, '\n');
                cout << endl;
            }

            if (ans == 'Y' || ans == 'y')
            {
                cout << "found" << endl;
                books.deleteNode(*current);

                bookType::decBookCount();
                bookCount = bookType::getBookCount();

                cout << setw(SIZE_MENUS / 4) << "" << "Book Deleted." << endl;
                cout << setw(SIZE_MENUS / 4) << "";
                system("pause");
                cout << endl;

                cout << setw(SIZE_MENUS / 4) << "" << "Would you like to delete another book? (Y/N): ";
                cin >> ans;
                cin.ignore(100, '\n');
                cout << endl;

                if (ans == 'Y' || ans == 'y')
                {
                    cout << endl;
                }
                if (ans == 'N' || ans == 'n')
                {
                    break;
                }
                else {
                    cout << setw(SIZE_MENUS / 3) << "" << "Please enter Y or N." << endl;
                    cout << setw(SIZE_MENUS / 3) << "" << "Would you like to delete another book? (Y/N): ";
                    cin >> ans;
                    cin.ignore(100, '\n');
                    cout << endl;
                }
            }
            else {
                break;
            }
        }
        else {
            break;
        }
    } while (choice != 'n' && choice != 'N');
}

So in my deleteBook function, I search for a book in the lookUpBook function (not shows as it seems to be working perfectly), and it returns a linkedListIterator, which I store into current. I then allow the user to choose whether or not to delete the book. If they say 'Y' or 'y' for yes, it prints out a "found" statement (seen in deleteBook as a check to see if it enters the print statement), and then the program crashes.

  • 4
    Unrelated: `void deleteBook(orderedLinkedList books)` accepts `books` by value. This is probably a bad idea. It will delete books from the copy made, not the original. While we're here, how much do you trust the `orderedLinkedList` copy constructor? – user4581301 Aug 08 '19 at 01:09
  • 2
    What does the stack look like when the program crashes? How about the integrity of the list itself. All too often problems in removal actually start with an incorrect insert. – user4581301 Aug 08 '19 at 01:11
  • 2
    Recommendation: separate the logic for finding the node to remove from the removal logic. It's much easier to test the two separately. Once you know they both work independently, find the node you want gone, and call remove on it. In general a function should do only one thing and your functions are doing a lot of things. This makes it hard to make sure they are doing everything right. Think of functions as building blocks Make simple functions and then put them together to make programs. – user4581301 Aug 08 '19 at 01:13
  • 2
    Unrelated: the [community addition in this answer](https://stackoverflow.com/a/22122095/4581301) may help you greatly reduce the number of different cases you need to handle when removing a node. – user4581301 Aug 08 '19 at 01:15
  • I trust the copy constructor, as it is straight from the textbook we are using for my class. I can send the linked list as pointers, but for now I just want it to stop crashing. Also, idk what the stack looks like (or what the stack is in general), but I know the integrity of the list is good, as I am able to print out all the data I need, I am able to search in the list, and I am able to add to the list (and search for the new item) Something I am unsure of, I am using a linkedListIterator current to search, and I call deleteNode(*current), could that be the issue? – Noopur G. Siroya Aug 08 '19 at 01:44
  • 1
    @NoopurG.Siroya "_I trust the copy constructor, as it is straight from the textbook we are using for my class_" - except, as far as I could see, you are using the default copy constructor which will just copy `nodeType`'s `current` and if that happens, you'll have two objects trying to use and release the same resource later. – Ted Lyngmo Aug 08 '19 at 01:57
  • ohhhhh ok I get it, I totally forgot to add & to the books in the function. Doing that fixed it all (i thought I added it because I added it to some other functions, but mustve slipped my mind). Thank you :D – Noopur G. Siroya Aug 08 '19 at 02:01
  • 1
    Odd. I wouldn't have expected that to have fixed anything if the copy constructor is working properly. You may have a second bug. I recommend testing the copy constructor a bit. It came out of a book, but I've seen some pretty wicked mistakes in text books over the years. – user4581301 Aug 08 '19 at 02:08
  • ok, ill test it out, see what the actual problem is, but at least now it is not crashing in that specific spot – Noopur G. Siroya Aug 08 '19 at 02:11
  • 1
    The stack is (usually--I should have suggested checking the backtrace. It is more correct. Not all systems use stacks) where all of the local variables go. You can use the backtrace to see where the program has been. Very helpful when debugging. The easiest way to check the stack is to run the program in a debugger. A debugger comes with just about every C++ development environment and if you somehow found one without a debugger, get a different one. When the program crashes, the debugger will halt it and allow you to see the backtrace. – user4581301 Aug 08 '19 at 02:14
  • 1
    In the backtrace you can see most of the variables that are in play. If they don't have the values you expect, that's probably the bug. It up to you to find out why it has the wrong value though. That's where the stepping functions of the debugger come in. You can use the debugger to make the program run at a speed that human can handle. you "step" through the program function by function, line by line, or even instruction by instruction and keep an eye on the suspect variables to see where the program does the unexpected. The debugger is one of the best productivity tools you'll ever use. – user4581301 Aug 08 '19 at 02:19
  • ooh ok thank you so much, i never knew visual studio could do things like this, this is very useful :D just to make sure, in visual studio, i am supposed to call the stack while debugging to see the backtrace, right? – Noopur G. Siroya Aug 08 '19 at 02:22
  • 1
    When you are debugging the stack usually sits in one of the panels in bottom of the window. I think it's bottom right, but I could be remembering wrong. [Here's good Familiarization/tutorial](https://learn.microsoft.com/en-us/visualstudio/debugger/debugger-feature-tour?view=vs-2019). Ha! Tutorial says exactly where to find it: bottom right. – user4581301 Aug 08 '19 at 03:54

0 Answers0