Зачем автор сначала удаляет объект на который указывает какой-то указатель, а потом пытается по этому указателю обратиться к объекту
Любое упоминание автор означает автора книги "Исскусство программирования на С++" Герберта Шилдта.
Книга 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 шт):
Так ведь не указатель удаляется, а объект, чей адрес он содержит. Простой пример:
int *a = new int(),
*b = new int(2),
*ptr = a;
delete ptr;
ptr = b;
delete ptr;
сначала ptr указывает на первый объект, затем удаляет этот объект, инициализируется адресом второго объекта и удаляет его тоже.
Точно также в вашем примере сделано, только вместо указателя там итератор(ведет себя как указатель), а удаление происходит в цикле и с условием... Кстати, для оформления вопроса gc.h и main.cpp не нужны.