This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  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.

     

    Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
    Please read our Terms, Conditions, and Privacy information.