Error EXC_BAD_ACCESS in list with SmartPointer

By : Daniele
Source: Stackoverflow.com
Question!

I'm facing this problem when I remove an element from my list.

Here my list.h:

class AwesomeList {
friend class Iteratore;
private:
class Nodo;

class SmartPointer {
public:
    Nodo* punt;

    SmartPointer(Nodo* n = 0): punt(n) {}
    SmartPointer(const SmartPointer& ptr): punt(ptr.punt) {}
    ~SmartPointer() {
        delete punt;
    }

    SmartPointer& operator=(const SmartPointer& ptr) {
        if (this != &ptr) {
            delete punt;
            punt = ptr.punt;
        }
        return *this;
    }
    bool operator==(const SmartPointer& ptr) const {
        return ptr.punt == punt;
    }
    bool operator!=(const SmartPointer& ptr) const {
        return ptr.punt != punt;
    }
    Nodo* operator->() const {
        return punt;
    }
    Nodo& operator*() const {
        return *punt;
    }
};

class Nodo {
public:
    T* value;
    SmartPointer next;

    Nodo(T* t = T(), const SmartPointer& ptr = SmartPointer()): value(t), next(ptr) {}
};

SmartPointer head;
SmartPointer tail;

public:
class Iteratore{
    friend class AwesomeList;
private:
    AwesomeList::SmartPointer punt;
public:
    bool operator==(const Iteratore& it) const {
        return it.punt == punt;
    }
    bool operator!=(const Iteratore& it) const {
        return it.punt != punt;
    }
    Iteratore& operator++() {
        if(punt != 0) punt = punt->next;
        return *this;
    }
    Iteratore& operator++(int) {
        if(punt != 0) punt = punt->next;
        return *this;
    }
    T* operator*() const {
        if (punt != 0) return punt->value;
    }
};

AwesomeList(const SmartPointer& ptr = 0): head(ptr), tail(0) {
    if (head != 0) {
        SmartPointer p = head;
        while (p != 0)
            p = p->next;
        tail = p;
    }
}
AwesomeList(const AwesomeList& list): head(list.head), tail(list.tail) {}

AwesomeList& operator=(const AwesomeList& list) {
    head = list.head;
    tail = list.tail;
}

int getSize() const {
    int count = 0;
    SmartPointer p = head;
    while (p != 0) {
        p = p->next;
        count++;
    }
    return count;
}
bool isEmpty() const {
    return getSize() == 0;
}
T* at(int pos) const {
    if (pos > -1 && pos < getSize()) {
        SmartPointer p = head;
        while (pos--) {
            p = p->next;
        }
        return p->value;
    } else return 0;
}
void add(const T& t) {
    if (head == 0) {
        head = SmartPointer(new Nodo(&(const_cast<T&>(t))));
        tail = head;
    } else {
        tail->next = SmartPointer(new Nodo(&(const_cast<T&>(t))));
        tail = tail->next;
    }
}
void remove(int pos) {
    if (pos > -1 && pos < getSize()) {
        SmartPointer newHead = head;
        SmartPointer p = newHead;
        head = 0;
        while (pos--) {
            add(*p->value);
            p = p->next;
        }
        p = p->next;
        while (p != 0) {
            add(*p->value);
            p = p->next;
        }
    }
}
void replace(int pos, T* t) {
    if (pos > -1 && pos < getSize()) {
        SmartPointer p = head;
        while (pos--)
            p = p->next;
        p->value = t;
    }
}
void replace(int pos, const T& t) {
    if (pos > -1 && pos < getSize()) {
        SmartPointer p = head;
        while (pos--)
            p = p->next;
        T& t_obj = const_cast<T&>(t);
        p->value = &t_obj;
    }
}

Iteratore begin() const {
    Iteratore it;
    it.punt = head;
    return it;
}
Iteratore end() const {
    Iteratore it;
    it.punt = 0;
    return it;
}
T* operator[](const Iteratore& it) const {
    return it.punt->value;
}
};

This are the tests I made:

AwesomeList<int> list = AwesomeList<int>();
list.add(1);
list.add(2);
list.add(3);
for (int i = 0; i < list.getSize(); i++)
    qDebug() <<*(list.at(i)) <<" ";

list.remove(-1);
for (int i = 0; i < list.getSize(); i++)
    qDebug() <<*(list.at(i)) <<" ";

list.remove(2);
for (int i = 0; i < list.getSize(); i++)
    qDebug() <<*(list.at(i)) <<" ";

list.replace(0, 5);
qDebug() <<"Replace in posizione 0";
auto cit = list.begin();
for (; cit != list.end(); cit++)
    qDebug() <<*(*cit) <<" ";

qDebug() <<"Size";
qDebug() <<list.getSize() <<endl;

This is the lines where errors appears:

  • AwesomeList::Nodo::~Nodo() + 16 (awesomelist.h:8)
  • AwesomeList::SmartPointer::~SmartPointer() + 42 (awesomelist.h:21)
  • AwesomeList::SmartPointer::~SmartPointer() + 21 (awesomelist.h:22)

Any help is appreciate. Thanks!

UPDATE

I solved my problem changing SmartPointer and Nodo classes like this:

SmartPointer(Nodo* n = 0): punt(n) {
        if (punt) punt->references++;
    }
    SmartPointer(const SmartPointer& ptr): punt(ptr.punt) {
        if (punt) punt->references++;
    }
    ~SmartPointer() {
        if (punt) {
            punt->references--;
            if (punt->references == 0) delete punt;
        }
    }

    SmartPointer& operator=(const SmartPointer& ptr) {
        if (this != &ptr) {
            Nodo* n = punt;
            punt = ptr.punt;
            if (punt) punt->references++;
            if (n) {
                n->references--;
                if (n->references == 0) delete n;
            }
        }
        return *this;
    }
    bool operator==(const SmartPointer& ptr) const {
        return ptr.punt == punt;
    }
    bool operator!=(const SmartPointer& ptr) const {
        return ptr.punt != punt;
    }
    Nodo* operator->() const {
        return punt;
    }
    Nodo& operator*() const {
        return *punt;
    }
};

class Nodo {
public:
    T* value;
    SmartPointer next;
    int references;

    Nodo(T* t = T(), const SmartPointer& ptr = SmartPointer()): value(t), next(ptr), references(0) {}
};
By : Daniele


Answers
Sorry but I don't understand the ratio beneath your SmartPointer class.

It carry a pointer to a Nodo and delete it with the constructor. Good.

But, if I'm not wrong

(1) when you create a SmartPointer with copy constructor, you copy the pointer from the SmartPointer copied so you have two object with a punt with the same value; when you destroy the two objects, you call delete two times over the same pointer; this can crash the program

(2) when you call operator=, you have the same problem with the copy constructor but, moreover, you don't delete the old pointed value

By example, look at add()

    head = SmartPointer(new Nodo(&(const_cast<T&>(t))));
    tail = head;

You create a temporary SmartPointer object initializing it with new Nodo(&(const_cast<T&>(t))). Next you copy this temporary object in head, so both head and the temporary object are carrying the same not-NULL pointer. Now the temporary object is destroyed, so the memory pointed by punt is deleted but head (his punt) continue to point to a memory area that is deleted. Now you copy head in tail, and you have both head and tail that are pointing to the same deleted area.

Look at the else case

    tail->next = SmartPointer(new Nodo(&(const_cast<T&>(t))));
    tail = tail->next;

In this case, tail->next (and take in count that take point to a deleted area) receive a pointer from a temporary object that delete it. So you write in a deleted area a pointer that is immediately deleted.

I hope it is clear how much all this is dangerous.

Suggestion: redesign SmartPointer class.

p.s.: sorry for my bad English.

By : max66


This video can help you solving your question :)
By: admin