Search code examples
c++pointersdangling-pointer

How to look for dangling pointers?


I'm trying to build a class for a linked list in C++. My professor says that I have a dangling pointer in my code. I've spent hours looking for it but I just can't find it no matter what. I tried asking a friend and they couldn't find it either. Is there some way of looking for dangling pointers or identifying them in code? I'll attach my code in case it's something obvious but I honestly do not know where this dangling pointer is. I'm aware this code isn't the best but it's passed all my tests and my professor's except for the fact that there's a dangling pointer. I've also heard of something called smart pointers but I don't think we're expected to use that and I don't know what those are. Any help finding this pointer is appreciated.

#include "IntList.h"
    #include <ostream>
    using namespace std;

    IntList::IntList() {
        head = nullptr;
        tail = nullptr;
    }

    IntList::IntList(const IntList &cpy) {
        
        if(cpy.head == nullptr) {
            head = nullptr;
            tail = nullptr;
        }
        else {
            IntNode *currNode = cpy.head;
            IntNode *temp = cpy.head;
            head = new IntNode(0);
            head->value = temp->value;
            currNode = head;
            temp = temp->next;

            while(temp != nullptr) {
                currNode->next = new IntNode(0);
                currNode = currNode->next;
                currNode->value = temp->value;
                currNode->next = nullptr;
                temp = temp->next;
            }
            tail = currNode;
        }
    }

    IntList::~IntList() {
        clear();
    }

    void IntList::push_front(int value) {
        IntNode *temp = new IntNode(value);
        if(head == nullptr) {
            head = temp;
            tail = head;
        }
        
        else {
            temp->next = head;
            head = temp;
        }
    }

    void IntList::pop_front() {
        if(empty()) {
            return;
        }
        else if(head->next == nullptr) {
            delete head;
            head = nullptr;
            tail = nullptr;
        }
        else {
            IntNode *temp = head;
            head = head->next;
            delete temp;
            temp = nullptr;
        }
    }

    void IntList::push_back(int value) {
        IntNode *temp = new IntNode(value);
        if(tail != nullptr) {
            tail->next = temp;
        }
        tail = temp;
        if(head == nullptr) {
            head = tail;
        }
    }

    bool IntList::empty() const {
        if(head == nullptr) {
            return true;
        }
        return false;
    }

    const int & IntList::front() const {
        return head->value;
    }

    const int & IntList::back() const {
        return tail->value;
    }

    void IntList::clear() {
        IntNode *currNode = head;
        IntNode *temp = currNode;
        // unsigned i = 1;
        while(currNode != nullptr) {
            currNode = currNode->next;
            temp->next = nullptr;
            delete temp;
            temp = currNode;
            // cout << "deleted node " << i << endl;
            // i++;
        }
        head = nullptr;
        tail = nullptr;
    }

    void IntList::selection_sort() {
        IntNode *loc = nullptr;
        IntNode *temp = new IntNode(0);
        IntNode *currNode = nullptr;
        IntNode *swap = nullptr;
        int min = 0;

        if(head == nullptr) {
            return;
        }
        else if(head->next == nullptr) {
            return;
        }
        else {
            for(currNode = head; currNode != nullptr; currNode = currNode->next) {
                loc = currNode;
                min = currNode->value;
                for(swap = currNode; swap != nullptr; swap = swap->next) {
                    if(swap->value < min) {
                        min = swap->value;
                        loc = swap;
                    }
                }
                temp->value = currNode->value;
                currNode->value = loc->value;
                loc->value = temp->value;
            }
        }
        
        delete temp;
        temp = nullptr;
    }

    void IntList::insert_ordered(int value) {
        if(head == nullptr || head->value >= value) {
            push_front(value);
        }
        else if(tail->value <= value) {
            push_back(value);
        }
        else {
            IntNode *newNode = new IntNode(value);
            for(IntNode *currNode = head; currNode != tail; currNode = currNode->next) {
                if(currNode->next->value > newNode->value) {
                    newNode->next = currNode->next;
                    currNode->next = newNode;
                    break;
                }
            }
        }
    }

    void IntList::remove_duplicates() {
        if(head == nullptr) {
            return;
        }
        else if(head->next == nullptr) {
            return;
        }
        else {
            IntNode *temp = head;
            for(IntNode *currNode = head; currNode != nullptr; currNode = currNode->next) {
                temp = currNode;
                for(IntNode *check = currNode->next; check != nullptr; check = check->next) {
                    if(check->value == currNode->value) {
                        temp->next = check->next;
                        if(check == tail) {
                            tail = temp;
                        }
                        if(check->next != nullptr) {
                            check->next = nullptr;
                        }
                        
                        delete check;
                        check = temp;
                    }
                    temp = check;
                }
            }
        }
    }

    IntList & IntList::operator=(const IntList &rhs) {
        IntList *newList = new IntList(rhs);
        swap(newList->head, head);
        newList->clear();
        delete newList;
        return *this;
    }

    ostream & operator<<(ostream &out, const IntList &rhs) {
        IntNode *currNode = rhs.head;
        while(currNode != nullptr) {
            out << currNode->value;
            currNode = currNode->next;
            if(currNode != nullptr) {
                out << " ";
            }
        }
        return out;
    }

    IntNode * IntList::min(IntNode *min) {
        return nullptr;
    }

    void IntList::copy(const IntList &) {}

I've tried deleting memory when appropriate and I usually assign pointers to nullptr after just to be certain they aren't dangling. I genuinely don't know what I'm doing wrong and it passes tests, but apparently after prolonged use it causes undefined behavior.


Solution

  • The operator= is bugged. Here's a test which shows the bug

    int main()
    {
        IntList x;
        x.push_back(1);
        x.push_back(2);
        IntList y;
        y.push_back(3);
        y.push_back(4);
        x = y;
        cout << x.back() << '\n';
    }
    

    This outputs (for me) -572662307 instead of the expected 4. The reason for the bug is the failure of operator= to update the tail member variable. I guess you could call that a dangling pointer.

    If you have a copy constructor and a swap member function then the simple and reliable way to implement operator= is to use the copy and swap idiom. It kind of looks like you were trying that, but if so then you made mistakes.

    To answer the question asked, no there is no foolproof way to identify dangling pointers. Try not to use them (i.e. pointers) in the first place is the best advice. But while you are learning you obviously have to go through the pain.