|
Smart Pointer Class
Submitted by |
A lot of the nasty C++ runtime errors occur because of pointer logic errors. A
good way to avoid many of them is to start using smart pointers. For those of
you who haven't heard of them, smart pointers are basically classes designed to
act and be used like pointers, but which do more stuff and can take care of a
lot of problems. For example, smart pointers save you a lot of typing and
thinking about handling exceptional situations in try{ } catch { cleanup } type
things.Take a look at www.boost.org (specifically
here) and other places. You also have
some smart pointer classes declared in your <memory> header, which you can read
more about here: http://ootips.org/yonat/4dev/smart-pointers.html.
When you read these documents, especially the latter one I mentioned, you see a
lot of different pointer types, each of which does a different thing. Sometimes
it helps to roll your own classes, and while coding my engine Aurora, website is not ready yet ), I have coded a bunch of
components for use in the engine, some merely for practice, others with
immediate practical uses in mind. One of these was auAccessor, which has been
good for both practice and practical purposes :-)
The auAccessor class is technically a reference-linked smart pointer. I decided
to write a smart pointer that could take care of more problems for me than other
smart pointers I've seen. Here are some of its capabilities:
It throws an error when you try to access NULL memory.
It creates new objects (by calling New() method ).
It maintains doubly linked lists of auAccessors pointing to the same object.
These lists are managed behind the scenes, as objects are created and destroyed,
pointers are reassigned, and so on. Garbage collection, etc. is automatic.
It is non-intrusive (doesn't add data members to the object.)
It can be reassigned to point to another object.
You can call Delete() method to delete the object pointed to, and this will not
only delete it but also setall the auAccessors still pointing to the deleted
object to point to NULL. They will then throw if misused.
Ability to Copy() the object, creating a new object, a copy, to which the
auAccessor will now point.
And other stuff...
As you can see, this smart pointer performs most of the tasks other smart
pointers do, and avoids a lot of problems, such as trying to use an object after
it has been deleted. In my opinion it's better than copy on write pointers
because it gives you the option to copy instead of forcing it on you, it
provides more intuitive, less simple solutions than auto_ptr does. In any case,
I rolled my own class and here's what I came up with. Maybe it will be useful to
you.
The code for the class is below.
Warning: the file below will not compile in your project straight away. You will
have to change one or two minor things in it. For example, instead of throwing
new auError, which is an error class in my engine, you can throw some other
object which you define.
This class does add a little overhead to functions such as assignment of a
pointer, which would otherwise have virtually no overhead. But it's always
constant-time overhead (except for setting a pointer = 0) and in fact I would
say most of it is just doing the "clean programming" which you would code anyway
if you had the time. You can add more stuff if you like to this class, like a
function returning the length of the array or record allocated, and so on.
If you find any errors or have any ideas, comments, suggests or threats, let me
know. You can contact me at contactgreg@hotmail.com. Tell me how you like it,
in fact. I want to hear from you! You can also comment and banter below. :-)
Cheers,
Greg Magarshak
|
Download Associated File: smclass.txt (7,290 bytes)
#ifndef AU_ACCESSOR
#define AU_ACCESSOR
/********************\
* auAccessor Class *
\********************/
template <class T>
class auAccessor
{
friend class auNullAcessorClass;
protected:
T* Ptr;
auAccessor *Prev, *Next;
auInt m_Size;
public:
void Delete(); // Deletes the object/array being pointed to. Resets
// this and all auAccessors pointing there to NULL.
void Remove(); // Stops pointing to the current address, points to NULL.
void Assign(auAccessor &A); // Assigns a new place to point to
void New(long Count=1); // Creates a new object or array on the heap.
void Copy(auInt Extra=0); // Creates a copy of the current object or array
// being pointed to, and starts pointing there.
// Adds extra elements if necessary.
void CopyElements(auAccessor &A, auUInt SourceBegin, auUInt SourceEnd, auUInt DestBegin); // Copies
// elements from one array to another, clips overflows
inline auInt ArraySize(); // Returns the copy of the size of the array being
// pointed to. A single object is an array of 1 element.
inline auAccessor(); // Constructor. Initializes values to 0.
inline auAccessor(auAccessor &A); // Copy constructor. Calls Assign(A);
inline ~auAccessor(); // Destructor. Calls Remove();
inline T& operator*();
inline T* operator->();
inline T* operator+(long Index);
inline T& operator[](long Index);
inline T& operator==(auAccessor Compare); // compare pointers
//inline T& operator==(auNullAccessor Compare) // if you really want to write "== auNull" :-)
inline operator bool(); // Used to test if pointer is NULL
inline auAccessor& operator=(auAccessor &A);
inline T* Address(); // Only if you want to hack (for efficiency?).
inline void AddressAssign(T* Addr, auInt Count); // Only if you want to hack (for external data?).
//inline void operator delete(void* pmem)
};
//auNullAccessor auNull;
template<class T>
void auAccessor<T>::Delete() // Deletes the object/array being pointed to.
// Resets this and all auAccessors pointing there to NULL.
{
auAccessor* a;
if (Ptr)
{
if (m_Size > 1)
delete[] Ptr;
else
delete Ptr;
// Clear all previous pointers in doubly linked list...
a = this; // start from the current one
while (a)
{
a->Ptr = 0;
a = a->Prev;
}
a = Next; // start from the next one
while (a)
{
a->Ptr = 0;
a = a->Next;
}
}
m_Size = 0;
}
template<class T>
void auAccessor<T>::Remove() // Stops pointing to the current address,
points to NULL.
{
// Remove this object from its current doubly linked list
if (Prev)
Prev->Next = Next;
if (Next)
Next->Prev = Prev;
// If it was already a single object, delete it.
if (!Prev && !Next)
Delete();
// And we're clean again :-)
Ptr = 0;
Prev = 0; Next = 0;
m_Size = 0;
}
template<class T>
void auAccessor<T>::Assign(auAccessor &A) // Assigns a new place to point
to
{
// Remove this object from its current doubly linked list
Remove();
// Add this object into A's doubly linked list
// A will be Prev.
Prev = &A;
Next = A.Next;
if (A.Next)
A.Next->Prev = this;
A.Next = this;
Ptr = A.Ptr;
m_Size = A.m_Size;
}
template<class T>
void auAccessor<T>::New(long Count) // Creates a new object or array on
the heap.
{
Remove();
// Allocate new data on the free store.
if (Count == 1)
Ptr = new T;
else
Ptr = new T[Count];
m_Size = Count;
};
template<class T>
void auAccessor<T>::Copy(auInt extra) // Creates a copy of the current
// object or array being pointed to, and starts pointing there.
{
// Special case.
if (!Ptr) // sound the alarm. :-)
throw new auError(AU_STDERROR_MISSING);
if (!Prev && !Next)
return; // the only copy, anyway.
// Save the pointer. The data won't be deleted.
T* p;
p = Ptr;
// Create new data and copy to it.
New(m_Size+extra);
memcpy(Ptr, p, sizeof(T) * m_Size);
// m_Size remains the same. Unless something's weird. :-)
}
template<class T>
auInt auAccessor<T>::ArraySize() // Returns the copy of the size of the
array being pointed to. A single object is an array of 1 element.
{
return m_Size
}
template<class T>
void auAccessor<T>::CopyElements
(auAccessor &A,
auUInt sb, auUInt se,
auUInt db) // Copies elements from one array to another
{
if (!Ptr) // sound the alarm. :-)
throw new auError(AU_STDERROR_MISSING);
if (!A.Ptr) // sound the alarm. :-)
throw new auError(AU_STDERROR_MISSING);
// Bounds checks
if (db >= m_Size)
throw new auError(AU_STDERROR_PARAMS);
if (sb >= A.m_Size)
throw new auError(AU_STDERROR_PARAMS);
// Bounds adjusting
if (se >= A.m_Size)
se = A.m_Size-1;
if (db+(se-sb) >= A.m_Size)
se = m_Size-1-(db-sb);
memcpy(Ptr+db, A.Ptr+sb, se-sb+1);
}
template<class T>
auAccessor<T>::auAccessor() // Constructor. Initializes values to 0.
{
// Be kind, rewind. :-)
Ptr = 0;
Prev = 0;
Next = 0;
m_Size = 0;
}
template<class T>
auAccessor<T>::auAccessor(auAccessor &A) // Copy constructor. Calls
Assign(A);
{
Assign(A);
}
template<class T>
auAccessor<T>::~auAccessor() // Destructor. Calls Remove();
{
Remove();
}
template<class T>
T& auAccessor<T>::operator*()
{
if (!Ptr) throw new auError(AU_STDERROR_MISSING);
return *Ptr;
}
template<class T>
T* auAccessor<T>::operator->()
{
if (!Ptr) throw new auError(AU_STDERROR_MISSING);
return Ptr;
}
template<class T>
T* auAccessor<T>::operator+(long Index)
{
if (!Ptr) throw new auError(AU_STDERROR_MISSING);
if (Index < 0) throw new auError(AU_STDERROR_BOUNDS);
if (Index >= m_Size) throw new auError(AU_STDERROR_BOUNDS);
return Ptr+Index;
}
template<class T>
T& auAccessor<T>::operator[](long Index)
{
if (!Ptr) throw new auError(AU_STDERROR_MISSING);
if (Index < 0) throw new auError(AU_STDERROR_BOUNDS);
if (Index >= m_Size) throw new auError(AU_STDERROR_BOUNDS);
return Ptr[Index];
}
template<class T>
T& auAccessor<T>::operator==(auAccessor Compare) // compare pointers
{
return (Ptr == Compare.Ptr);
}
//
//inline T& auAccessor<T>::operator==(auNullAccessor Compare) // if you really want to write "== auNull" :-)
//{
// return (Ptr == Compare.Ptr);
//}
template<class T>
auAccessor<T>::operator bool() // Used to test if pointer is NULL
{
if (Ptr) return true;
else return false;
}
template<class T>
auAccessor<T>& auAccessor<T>::operator=(auAccessor &A)
{
Assign(A);
return A;
}
template<class T>
T* auAccessor<T>::Address() // Only if you want to hack (for efficiency?).
{
return Ptr;
}
template<class T>
void auAccessor<T>::AddressAssign(T* p, auInt count) // Only if you want to hack (for efficiency?).
{
Remove();
Ptr = p;
m_Size = count;
}
|
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|