Зачем автор сначала удаляет объект на который указывает какой-то указатель, а потом пытается по этому указателю обратиться к объекту

Любое упоминание автор означает автора книги "Исскусство программирования на С++" Герберта Шилдта.

Книга 2004 года, поэтому возможно какие-то действия из реализации сборщика устарели. Я начинающий плюсовик, поэтому прощу помочь с пониманием.

Проблема: Не могу понять логику следующего действия. Есть gc.h файл в котором описан сборщик мусора с помощью алгоритма - подсчёт ссылок. Так вот в методе GCPtr::collect() автор сначала вызывает метод remove() для STL контейнера list для того чтобы удалить неиспользуемый объект класса GCInfo, в котором прописан указатель memPtr указывающий на выделенную память. Так вот после того как объект класса GCInfo удалён, у нас больше нет доступа к той ячейки памяти через объект, тем не менее автор далее пытается получить адресс указателя на ту ячейку памяти через объект который был уничтожен. Естественно Visual Studio кидает мне Debug Assertion и завершается аварийно.

Код где обнаружена проблема:

//Gathering garbage returns true if at least one object was cleaned
template<class T, int size>
bool GCPtr<T, size>::collect()
{
    bool memfreed = false;

#ifdef DISPLAY
    cout << "Before garbage collection for ";
    showlist();
#endif // DISPLAY

    typename list<GCInfo<T> >::iterator p;
    do
    {
        /*Looks through gclist in order
        to find pointers that point
        at nothing*/
        for (p = gclist.begin(); p != gclist.end(); p++)
        {
            //Skip if pointer is used
            if (p->refcount > 0) continue;

            memfreed = true;

            //Delete unused element from gclist

            gclist.remove(*p);

            //Frees memory till GCPtr is equal to null
            //После gclist.remove(*p) доступ к memPtr через объект класса GCInfo невозможен
                if (p->memPtr) //Но автор продолжает обращаться к членам объекта через указатель
                {
                    if (p->bIsArray)
                    {
#ifdef DISPLAY
                        cout << "Deleting array of size "
                            << p->arraySize << endl;
#endif // DISPLAY
                        delete[] p->memPtr; //delete the array
                    }
                    else
                    {
#ifdef DISPLAY
                        cout << "Deleting: "
                            << *(T*)p->memPtr << "\n";
#endif // DISPLAY
                        delete p->memPtr; //delete single element
                    }
                }
            //Refresh search
            break;
        }
    } while (p != gclist.end());
#ifdef DISPLAY
    cout << "After garabage collection for ";
    showlist();
#endif // DISPLAY
    return memfreed;
}

Все решение gc.h:

#include <iostream>
#include <list>
#include <typeinfo>
#include <cstdlib>
//#include <iterator> not sure do i need it

using namespace std;
//To see how the garabage collector works, uncomment the macro below
#define DISPLAY

class OutOfRangeExc
{
    //Any functional features that are neccessary for your application
};

//Iter pointers don't participate in the garabage collection process

template <class T> class Iter
{
    T* ptr; //Current pointer
    T* end; //Points at element that's set after the last one
    T* begin; //Points at the beginning of allocated array
    unsigned lenght; 
public:
    Iter()
    {
        ptr = end = begin = NULL;
        lenght = 0;
    }

    Iter(T* p, T* first, T* last)
    {
        ptr = p;
        end = last;
        begin = first;
        lenght = last - first;
    }
    // returns the lenght of the succession
    unsigned size() { return lenght; }

    //returns the value that's pointed by ptr
    // doesn't allow to get out of the range
    T& operator*()
    {
        if ((ptr >= end) || (ptr < begin)) 
            throw OutOfRangeExc();
        return *ptr;
        
    }

    //returns the adress of ptr
    //doesn't allow to get out of the range
    T* operator->()
    {
        if ((ptr >= end) || (ptr < begin))
            throw OutOfRangeExc();
        return ptr;
    }

    //Prefix ++
    Iter operator++ ()
    {
        ptr++;
        return *this;
    }

    //Prefix --
    Iter operator-- ()
    {
        ptr--;
        return *this;
    }

    //Postfix ++
    Iter operator++(int notused)
    {
        T* tmp = ptr;
        ptr++;
        return Iter<T>(tmp, begin, end);
    }

    //Postfix--
    Iter operator--(int notused)
    {
        T* tmp = ptr;
        ptr--;
        return Iter<T>(tmp, begin, end);
    }

    //returns a link to an object
    // doesn't let to get out of the range
    T& operator[] (int i)
    {
        if ((i < 0) || (i >= (end - begin)))
            throw OutOfRangeExc();
        return ptr[i];
    }

#pragma region Comparison Operations
    bool operator==(Iter op2)
    {
        return ptr == op2.ptr;
    }

    bool operator!=(Iter op2)
    {
        return ptr != op2.ptr;
    }

    bool operator<(Iter op2)
    {
        return ptr < op2.ptr;
    }

    bool operator<=(Iter op2)
    {
        return ptr <= op2.ptr;
    }

    bool operator>(Iter op2)
    {
        return ptr > op2.ptr;
    }
    bool operator>=(Iter op2)
    {
        return ptr >= op2.ptr;
    }
#pragma endregion

    //Diminish int
    Iter operator-(int n)
    {
        ptr -= n;
        return *this;
    }

    //Add int
    Iter operator+(int n)
    {
        ptr += n;
        return *this;
    }

    //returns a number of elements between two Iter pointers
    int operator-(Iter<T>& itr2)
    {
        return ptr - itr2.ptr;
    }
};


/*This class defines an element that's remembered
 by the garbage collector's info list*/

template <class T> class GCInfo
{
public:
    unsigned refcount; //Current reference count;
    T* memPtr; //Points at the allocated memory

    /* bIsArray is equal true if memPtr points at
     an allocated array, otherwise false */
    bool bIsArray;

    /* is memPtr points at the allocated array, then
     arraySize contains its size */
    unsigned arraySize; // lenght or size of the array

    /* Here, mPtr points at an allocated memory.
     If it is array, size is the array's lenght */
    GCInfo(T* mPtr, unsigned size = 0)
    {
        refcount = 1;
        memPtr = mPtr;
        if (size != 0)
            bIsArray = true;
        else
            bIsArray = false;

        arraySize = size;
    }
};

/* Overriden == allows to compare GCInfo objects
 Is is neccessary for the class list from the STL library*/
template <class T> 
bool operator == (const GCInfo<T>& ob1,
    const GCInfo<T>& ob2)
{
    return (ob1.memPtr == ob2.memPtr);
}

/* GCPtr realizes a type of pointer that is used for
gathering "garbage" and clear unused memory.
   GCptr must be used solely to point at memory which
is allocated dynamically via new operator.
    if it's applied for a link to the array, it defines
the array's sizze*/
template <class T, int size = 0> class GCPtr
{
    //gclist supports a list of garbage collection
    static list<GCInfo<T>> gclist;

    /*addr points at the allocated memory which
    is pointed at by this GCPtr*/
    T* addr;

    /*if this GCPtr points at the allocated array,
    bIsArray is true, otherwise false*/
    bool bIsArray;

    /*if this GCPtr points at the dynamically 
    allocated array, arraySize contains the 
    array's size*/
    unsigned arraySize;
    static bool bFirst; //true when the first GCPtr is created

    //returns iterator for pointer info in the gclist
    typename list<GCInfo<T>>::iterator findPtrInfo(T* ptr);
    
public:
    //defines a type of iterator for GCPtr<T>
    typedef Iter<T> GCIterator;

    //Constuctor for initialized and uninitialized objects
    GCPtr(T* t = NULL);
    //Copy constructor
    GCPtr(const GCPtr& ob);
    //Destructor
    ~GCPtr();

    /*Gathering garbage. if at least one object was
    deleted, returns true*/
    static bool collect();

    //Override = GCPtr
    T* operator=(T* t);
    //Override GCPtr = GCPtr
    GCPtr& operator=(GCPtr& rv);

    /*returns a reference to the object that's
    pointed at by this GCPtr*/
    T& operator*()
    {
        return *addr;
    }

    //returns the address that's pointed at by
    T* operator->() { return addr; }

    //returns a reference to the object at i index
    T& operator[](int i)
    {
        return addr[i];
    }
    
    //conversion function
    operator T* () { return addr; }

    //returns Iter for the beginning of the allocated memory
    Iter<T> begin()
    {
        int osize;
        if (bIsArray) osize = arraySize;
        else osize = 1;

        return Iter<T>(addr, addr, addr + osize);
    }
    
    /*return Iter for the element that's following after
        the last element that's allocated in memory*/
    Iter<T> end()
    {
        int osize;
        if (bIsArray) osize = arraySize;
        else osize = 1;
        return Iter<T>(addr + osize, addr, addr + osize);
    }

    //returns the gclist's size for this GCPtr
    static size_t gclistSize() { return gclist.size(); }

    //show gclist
    static void showlist();

    //Clear gclist when the programm is off
    static void shutdown();
};

template<class T, int size>
typename list<GCInfo<T>>::iterator GCPtr<T, size>::findPtrInfo(T* ptr)
{
    typename list<GCInfo<T> >::iterator p;

    //Find ptr in gclist
    for (p = gclist.begin(); p != gclist.end(); p++)
        if (p->memPtr == ptr)
            return p;
    return p;
}

template<class T, int size>
inline GCPtr<T, size>::GCPtr(T* t)
{
    //register shutdown() as a end function
    if (bFirst) atexit(shutdown);
    bFirst = false;

    typename list<GCInfo<T>>::iterator p;
    p = findPtrInfo(t);

    /*if t is already in the gclist, increments its
    refcounter, otherwise adds in the list*/
    if (p != gclist.end())
        p->refcount++;
    else
    {
        //creates and remembers a new element
        GCInfo<T> gcObj(t, size);
        gclist.push_front(gcObj);
    }

    addr = t;
    arraySize = size;
    if (size > 0) bIsArray = true;
    else bIsArray = false;

#ifdef DISPLAY
    cout << "Constructing GCPtr. ";
    if (bIsArray)
        cout << "Size is " << arraySize << endl;
    else
        cout << endl;
#endif // DISPLAY
}

template<class T, int size>
GCPtr<T, size>::GCPtr(const GCPtr& ob)
{
    typename list<GCInfo<T> >::iterator p;
    p = findPtrInfo(ob.addr);
    p->refcount++; //increments the ref counter

    addr = ob.addr;
    arraySize = ob.arraySize;
    if (arraySize > 0) bIsArray = true;
    else bIsArray = false;

#ifdef DISPLAY
    cout << "Construction copy.";
    if (bIsArray)
        cout << " Size is " << arraySize << endl;
    else
        cout << endl;
#endif // DISPLAY

    /*Gather "garbage", when pointer gets out
    of scope.*/
    collect();
}

template<class T, int size>
GCPtr<T, size>::~GCPtr()
{
    typename list<GCInfo<T> >::iterator p;

    p = findPtrInfo(addr);
    if (p->refcount) p->refcount--; //decrement refcounter

#ifdef DISPLAY
    cout << "GCPtr going out of scope. \n";
#endif // DISPLAY

}

//Gathering garbage returns true if at least one object was cleaned
template<class T, int size>
bool GCPtr<T, size>::collect()
{
    bool memfreed = false;

#ifdef DISPLAY
    cout << "Before garbage collection for ";
    showlist();
#endif // DISPLAY

    typename list<GCInfo<T> >::iterator p;
    do
    {
        /*Looks through gclist in order
        to find pointers that point
        at nothing*/
        for (p = gclist.begin(); p != gclist.end(); p++)
        {
            //Skip if pointer is used
            if (p->refcount > 0) continue;

            memfreed = true;

            //Delete unused element from gclis

            gclist.remove(*p);

            //Frees memory till GCPtr is equal to null
            
                if (p->memPtr)
                {
                    if (p->bIsArray)
                    {
#ifdef DISPLAY
                        cout << "Deleting array of size "
                            << p->arraySize << endl;
#endif // DISPLAY
                        delete[] p->memPtr; //delete the array
                    }
                    else
                    {
#ifdef DISPLAY
                        cout << "Deleting: "
                            << *(T*)p->memPtr << "\n";
#endif // DISPLAY
                        delete p->memPtr; //delete single element
                    }
                }
            //Refresh search
            break;
        }
    } while (p != gclist.end());
#ifdef DISPLAY
    cout << "After garabage collection for ";
    showlist();
#endif // DISPLAY
    return memfreed;
}

template <class T, int size>
T* GCPtr<T, size>::operator=(T* t)
{
    typename list<GCInfo<T> >::iterator p;

    //First of all, decrements the ref counter
    p = findPtrInfo(addr);
    p->refcount--;

    /*Next, if new adress already exists,
    increments the ref counter. Otherwise,
    creates a new element in the gclist*/
    p = findPtrInfo(t);
    if (p != gclist.end())
        p->refcount++;
    else
    {
        //Creates and remembers a new element
        GCInfo<T> gcObj(t, size);
        gclist.push_front(gcObj);
    }

    addr = t; // remember the address
    return t;
}

template <class T, int size>
GCPtr<T, size>& GCPtr<T, size>::operator=(GCPtr& rv)
{
    typename list<GCInfo<T> >::iterator p;

    //Firts, decrements the ref counter
    p = findPtrInfo(addr);
    p->refcount--;

    //Next, increment the ref counter for new address
    p = findPtrInfo(rv.addr);
    p->refcount++;

    addr = rv.addr; // remembers the address

    return rv;
}

template<class T, int size>
void GCPtr<T, size>::showlist()
{
    typename list<GCInfo<T> >::iterator p;

    cout << "gclist<" << typeid(T).name() << ", "
        << size << ">: \n";

    cout << "memPtr    refcount    value\n";

    if (gclist.begin() == gclist.end())
    {
        cout << " -- Empty -- \n\n";
        return;
    }

    for (p = gclist.begin(); p != gclist.end(); p++)
    {
        cout << "[" << (void*)p->memPtr << "]"
            << "     " << p->refcount << " ";
        if (p->memPtr) cout << "     " << *p->memPtr;
        else cout << "  ---";
        cout << endl;
    }
    cout << endl;
}

template<class T, int size>
void GCPtr<T, size>::shutdown()
{
    if (gclistSize() == 0) return; //list is empty

    typename list<GCInfo<T> >::iterator p;

    for (p = gclist.begin(); p != gclist.end(); p++)
    {
        //set all ref counter to 0
        p->refcount = 0;
    }

#ifdef DISPLAY
    cout << "Before collecting for shutdonw() for "
        << typeid(T).name() << "\n";
#endif // DISPLAY

    collect();
#ifdef DISPLAY
    cout << "After collecting for shutdown() for "
        << typeid(T).name() << "\n";
#endif // DISPLAY

}


//Allocate memory for static variables
template <class T, int size>
list<GCInfo<T> > GCPtr<T, size>::gclist;

template <class T, int size>
bool GCPtr<T, size>::bFirst = true;

//Override = GCPtr poiinter
template <class T, int size>
T* GCPtr<T, size>::operator=(T* t);

//Override GCPtr = GCPtr
template <class T, int size>
GCPtr<T, size>& GCPtr<T, size>::operator=(GCPtr& rv);

main.cpp для теста:

#include <iostream>
#include "gc.h" //garbage collector


int main()
{
    
    GCPtr<int> ptr = new int(5);
    cout << *ptr;
}

Ответы (1 шт):

Автор решения: AR Hovsepyan

Так ведь не указатель удаляется, а объект, чей адрес он содержит. Простой пример:

int *a = new int(), 
    *b = new int(2), 
    *ptr = a;
delete ptr;
ptr = b;
delete ptr;

сначала ptr указывает на первый объект, затем удаляет этот объект, инициализируется адресом второго объекта и удаляет его тоже.

Точно также в вашем примере сделано, только вместо указателя там итератор(ведет себя как указатель), а удаление происходит в цикле и с условием... Кстати, для оформления вопроса gc.h и main.cpp не нужны.

→ Ссылка