#ifndef SPTRS_H #define SPTRS_H /////////////////////////////////////////////////////////////// // sptrs.h - smart pointer class with ref counting // // v4.0 // // Language: Visual C++, ver 6.0 // // Platform: Micron Dual pentium pro, Win NT 4.0, SP3 // // Application: CSE687, demonstration // // Author: Jim Fawcett, CST 2-187, Syracuse Univ // // (315) 443-3948, fawcett@ecs.syr.edu // // // // copyright (c) 1995-2000 // // all rights granted to users except right to publish // /////////////////////////////////////////////////////////////// /* sptr class operations: ====================== provide all the operations allowed for dumb C++ pointers, but add range checking and automated memory management construct smart pointers: ========================= sptr sp; // sptr allocated one object sptr sp = p; // capture contents of C++ pointer T p[3] = { t1, t2, t3 }; // initialize an array of T's then sptr sp(p,3); // construct initialized smart pointer sptr sp(10); // pointer allocated 10 objects sptr sp2 = sp1; // create new sptr to sp1's data smart pointer operations: ========================= int b = !sp; // returns 0 if null pointer, else 1 sp2 = sp1; // shallow assignment (no memory leak) sp = p; // assign C++ pointer to smart pointer T t2 = *sp = t1; // dereference sp contents T t2 = sp[4] = t1; // access sp contents through index sp += 3; // additive assignment of offset sp2 = sp1 + 5; // add offset sp -= 2; // subtractive assignment sp2 = sp1 - 3; // subtract offset ++sp; sp++; // pre and post-increments --sp; sp--; // pre and post-decrements resetting smart pointer parameters: =================================== sp.first(); // go to top of current block int s = sp.size(); // return size of allocated block sp.size(10); // reallocate new memory sp.showState(s); // display state of smart pointer */ // /////////////////////////////////////////////////////////////// // Build Process // /////////////////////////////////////////////////////////////// // Files Required: // // sptrs.hpp, sptrs.cpp // // // // Command Line Compiler Build: // // cl /GX /GZ /DTEST_SPTRS sptrs.cpp // /////////////////////////////////////////////////////////////// /* Maintenance History: ==================== ver 4.0 : 24 Feb 02 - removed class ptr - no real need for this ver 3.6 : 11 Feb 00 - added forward declaration needed so compilation would succeed with Visual C++ (originally developed with Borland C++) ver 3.5 : 05 Feb 97 - added derived fptr, fsptr which are fast inline indexers because they eliminate domain bounds checking ver 3.4 : 28 Nov 96 - test assign, init from an array, test self destruction ver 3.3 : 20 Nov 96 - eliminated redundant size information, fixed bug in reSize() ver 3.2 : 14 Aug 96 - fixed bug in ~ptr, e.g., failed to test for null top ver 3.1 : 1 Aug 96 - strengthed null pointer processing, revised state display ver 3.0 : 31 Jul 96 - defined assign from C++ pointer, improved safety w/ null ptrs ver 2.1 : 26 Apr 96 - added a destr which checks for dangling refs when counted obj goes out of scope ver 2.0 : 24 Apr 96 - incorporated reference counting objects into design ver 1.0 : 21 Apr 96 - first release Design Notes: ============= Goal of this design is to make smart pointers have syntax very close to native C++ pointers, but add semantics which monitor and provide error messages for domain errors, dangling pointers, and use of uninitialized pointers. This class uses reference counting to avoid dangling references and allocate memory instead of depending on client to provide it. Planned Modifications: ====================== Alter design to put all indexing responsibility on RCObjs. */ // /////////////////////////////////////////////////////////////// // declarations // /////////////////////////////////////////////////////////////// #include // exit() /////////////////////////////////////////////////////////////// // reference counting object // /////////////////////////////////////////////////////////////// template class sptr; template class RCobj { friend class sptr; public: RCobj(int size=1); ~RCobj(); void addRef() { ++refCnt; } void delRef(); int count() { return refCnt; } T& operator[](int n); int reSize(int size); #ifdef TEST_SPTRS static void showAlloc(int show) { _showAlloc = show ? 1 : 0; } #endif private: int _max; T *array; int refCnt; static int bytes_alloc; static int _showAlloc; static void killJob(char *s); RCobj(const RCobj &rco); RCobj& operator=(const RCobj &rco); }; template int RCobj::bytes_alloc = 0; #ifdef TEST_SPTRS template int RCobj::_showAlloc = 0; #endif template inline void RCobj::killJob(char *s) { cout << s << endl; exit(1); } // //----< constructor >------------------------------------------ template RCobj::RCobj(int size) { if(size <= 0) { array = 0; _max = 0; } else { array = new T[_max=size]; if(!array) RCobj::killJob("memory alloc failure in RCobj()"); bytes_alloc += _max*sizeof(T); } refCnt=0; } //----< copy constructor >------------------------------------- template RCobj::RCobj(const RCobj &rco) : _max(rco._max) { array = new T[_max]; if(!array) RCobj::killJob("memory alloc failure in RCobj()"); bytes_alloc += _max*sizeof(T); int i; for(i=0; i<_max; i++) array[i] = rco.array[i]; refCnt=0; } //----< destructor >------------------------------------------- template RCobj::~RCobj() { bytes_alloc -= _max*sizeof(T); #ifdef TEST_SPTRS if(_showAlloc) cout << " " << bytes_alloc << " remaining RCobj bytes allocated\n"; #endif delete [] array; if(refCnt) RCobj::killJob("RCobj going out of scope with dangling pointers"); } //----< delete reference to this object >---------------------- template void RCobj::delRef() { if(--refCnt == 0) { bytes_alloc -= sizeof(RCobj); #ifdef TEST_SPTRS if(_showAlloc) cout << "\n RCobj committing suicide\n " << bytes_alloc << " remaining RCobj bytes allocated\n"; #endif delete this; } } // //----< assignment >------------------------------------------- template RCobj& RCobj::operator=(const RCobj &rco) { bytes_alloc += (rco._max-_max)*sizeof(T); _max = rco._max; delete [] array; array = new T[_max]; if(!array) RCobj::killJob("memory alloc failure in RCobj::operator=()"); int i; for(i=0; i<_max; i++) array[i] = rco.array[i]; return *this; } //----< indexer >---------------------------------------------- template T& RCobj::operator[](int n) { if(array==0 || n<0 || _max <= n) RCobj::killJob("index out of bounds in RCobj::operator[]"); return array[n]; } //----< resize counted object >-------------------------------- template int RCobj::reSize(int size) { if(size == _max) return 1; if(size <= 0) { if(array) { delete [] array; array = 0; bytes_alloc -= _max*sizeof(T); } _max = 0; return 0; } T *temp = new T[size]; if(!temp) RCobj::killJob("memory allocation failure in RCobj::reSize()"); bytes_alloc += (size-_max)*sizeof(T); int i; for(i=0; i<_max; i++) if(i < _max) temp[i] = array[i]; else break; _max = size; delete [] array; array = temp; return 1; } // ////////////////////////////////////// // smart pointer class declaration // ////////////////////////////////////// template class sptr { public: sptr(const T* t, int size=1); sptr(int size=1); sptr(const sptr &p); virtual ~sptr(); sptr& operator=(const sptr &p); sptr& operator=(const T *p); T& operator*(); T* operator->(); virtual T& operator[](int n); virtual T operator[](int n) const; sptr& operator+=(int n); sptr operator+(int n); sptr& operator-=(int n); sptr operator-(int n); sptr& operator++(); sptr operator++(int); sptr& operator--(); sptr operator--(int); int operator!(); void first(); int size() const; int size(int size); void showState(char *s); static int alloc() { return (bytes_alloc + RCobj::bytes_alloc); } #ifdef TEST_SPTRS static void showAlloc(int show) { _showAlloc = show ? 1 : 0; } #endif protected: RCobj *top; // reference counting object T *rptr; // current value of address static int bytes_alloc; #ifdef TEST_SPTRS static int _showAlloc; #endif void alloc(int size=1); }; template int sptr::bytes_alloc = 0; #ifdef TEST_SPTRS template int sptr::_showAlloc = 0; #endif // /////////////////////////////////////////////////////////////// // derived smart pointer class that overrides indexing to // // eliminate bounds checking - intend to boost performance // /////////////////////////////////////////////////////////////// template class fptr : public sptr { public: fptr(const T* t, int size=1) : sptr(t,size) { } fptr(int size=1) : sptr(size) { } fptr(const sptr &p) : sptr(p) { } virtual T& operator[](int n) { return rptr[n]; } virtual T operator[](int n) const { return rptr[n]; } }; /////////////////////////////////////////////////////////////// // definitions of public member functions // /////////////////////////////////////////////////////////////// //----< point to top of allocated memory >--------------------- template inline void sptr::first() { top ? rptr = top->array : 0; } //----< return size of allocated memory >---------------------- template inline int sptr::size() const { return top ? top->_max : 0; } //----< request reallocation of this object's memory block >--- template int sptr::size(int size) { if(!top) { alloc(size); return 1; } // size_t is an unsigned type, used here to capture the offset // of rptr into the RCobj array which should always be non-negative size_t index = (size_t)(rptr-(top->array)); int ok = top->reSize(size); // allocate new memory rptr = (top->array) + index; // index into new memory return ok; } // //----< allocate new ref counted objects >--------------------- template void sptr::alloc(int size) { // assumes array has not already been allocated, e.g., // use for new ref counted objects only if(size > 0) { top = new RCobj(size); if(!top) RCobj::killJob("memory allocation failure in sptr()"); RCobj::bytes_alloc += sizeof(RCobj); rptr = top->array; top->_max = size; top->addRef(); } else { (RCobj*)top = 0; (T*)rptr = 0; } } //----< construct from a primitive pointer >------------------- template sptr::sptr(const T* t, int size) { alloc(size); int i; if(size >0) for(i=0; iarray)[i] = t[i]; } //----< construct array >-------------------------------------- template sptr::sptr(int size) { alloc(size); } //----< copy constructor >------------------------------------- template sptr::sptr(const sptr &p) { top = p.top; // this is a pointer so rptr = p.rptr; // do a shallow copy and top->addRef(); // don't allocate new memory } //----< destructor >------------------------------------------- template sptr::~sptr() { if(top) top->delRef(); #ifdef TEST_SPTRS if(_showAlloc) cout << " " << bytes_alloc << " remaining sptr bytes allocated\n"; #endif } // //----< assignment operator >---------------------------------- template sptr& sptr::operator=(const sptr &p) { if(this == &p) return *this; top->delRef(); // point away from last RCobj top = p.top; // this is a pointer rptr = p.rptr; // so do a shallow copy, top->addRef(); // don't allocate or return *this; // deallocate memory } //----< assignment operator >---------------------------------- template sptr& sptr::operator=(const T *p) { top->delRef(); // point away from last RCobj alloc(1); // create new RCobj top->array[0] = *p; // copy raw memory return *this; } //----< indexing operator >------------------------------------ template T& sptr::operator[](int n) { static T safe; if(top == 0) { cout << " dereferencing null pointer\n"; return safe; } if(top->_max==0) { cout << " dereferencing unallocated memory\n"; return safe; } int pos = rptr-(top->array)+n; if(pos<0 || top->_max <= pos) { cout <<" pointer dereference out of range\n"; return safe; } return rptr[n]; } // //----< indexing operator >------------------------------------ template T sptr::operator[](int n) const { static T safe; if(top == 0) { cout << " dereferencing null pointer\n"; return safe; } if(top->_max==0) { cout << " dereferencing unallocated memory\n"; return safe; } int pos = rptr-(top->array)+n; if(pos<0 || top->_max <= pos) { cout <<" pointer dereference out of range\n"; return safe; } return rptr[n]; } //----< dereferencing operator >------------------------------- template T& sptr::operator*() { return (this->operator[](0)); } //----< selection operator >----------------------------------- template T* sptr::operator->() { if(rptr == 0) { cout << "selecting with invalid pointer in operator->()\n"; exit(1); } return rptr; } //----< additive assignment >---------------------------------- template sptr& sptr::operator+=(int n) { rptr+=n; return *this; } //----< addition of integer offset >--------------------------- template sptr sptr::operator+(int n) { sptr sum = *this; return(sum += n); } // //----< subtractive assignment >------------------------------- template sptr& sptr::operator-=(int n) { rptr-=n; return *this; } //----< subtraction of integer offset >------------------------ template sptr sptr::operator-(int n) { sptr diff = *this; return (diff-=n); } //----< preincrement operator >-------------------------------- template sptr& sptr::operator++() { rptr++; return *this; } //----< postincrement operator >------------------------------- template sptr sptr::operator++(int) { sptr old = *this; rptr++; return old; } //----< predecrement operator >-------------------------------- template sptr& sptr::operator--() { rptr--; return *this; } //----< postdecrement operator >------------------------------- template sptr sptr::operator--(int) { sptr old = *this; rptr--; return old; } //----< is null? >--------------------------------------------- template int sptr::operator!() { return ((rptr == 0) ? 1 : 0); } // //----< display smart pointer state >-------------------------- template void sptr::showState(char *s) { cout << "-------------------------------------------\n"; cout << " state of smart pointer " << s << ":\n"; cout << " top = " << top << "\n"; cout << " rptr = " << rptr << "\n"; if(top) { cout << " array index = " << (rptr - (top->array)) << "\n"; cout << " RCobj::refCnt = " << top->refCnt << "\n"; cout << " RCobj::_max = " << top->_max << "\n"; } else cout << " no attached ref counting object\n"; cout << "-------------------------------------------\n"; cout << endl; cout.fill(' '); } #endif