#ifndef SPTRS_H #define SPTRS_H /////////////////////////////////////////////////////////////// // sptrs.h - smart pointer class with ref counting // // v3.6 // // 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: ========================= ptr sp; // ptr allocated one object ptr sp = p; // capture contents of C++ pointer T p[3] = { t1, t2, t3 }; // initialize an array of T's then ptr sp(p,3); // construct initialized smart pointer ptr sp(10); // pointer allocated 10 objects ptr sp2 = sp1; // create new ptr 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 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. Pointer representation is partitioned into two classes, ptr which points to single objects or arrays of single objects, and sptr which points to structured data or arrays of same. Both classes use 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 ptr; template class RCobj { friend class ptr; 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; } // /////////////////////////////////////////////////////////////// // pointer to unstructured data, no operator->() // /////////////////////////////////////////////////////////////// template class ptr { public: ptr(const T* t, int size=1); ptr(int size=1); ptr(const ptr &p); virtual ~ptr(); ptr& operator=(const ptr &p); ptr& operator=(const T *p); T& operator*(); virtual T& operator[](int n); virtual T operator[](int n) const; ptr& operator+=(int n); ptr operator+(int n); ptr& operator-=(int n); ptr operator-(int n); ptr& operator++(); ptr operator++(int); ptr& operator--(); ptr 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 ptr::bytes_alloc = 0; #ifdef TEST_SPTRS template int ptr::_showAlloc = 0; #endif // template class fptr : public ptr { public: fptr(const T* t, int size=1) : ptr(t,size) { } fptr(int size=1) : ptr(size) { } fptr(const ptr &p) : ptr(p) { } virtual T& operator[](int n) { return rptr[n]; } virtual T operator[](int n) const { return rptr[n]; } }; /////////////////////////////////////////////////////////////// // // // pointer to structured data, adds operator->() // // // // use all ptr operations including assigment // // only purpose is to add member selection operator // // // /////////////////////////////////////////////////////////////// template class sptr : public ptr { public: sptr(const T* t, int size=1) : ptr(t,size) { } sptr(int size=1) : ptr(size) { } sptr(const ptr &p) : ptr(p) { } virtual T* operator->(); }; template class fsptr : public sptr { public: fsptr(const T* t, int size=1) : sptr(t,size) { } fsptr(int size=1) : sptr(size) { } fsptr(const ptr &p) : sptr(p) { } virtual T& operator[](int n) { return rptr[n]; } virtual T operator[](int n) const { return rptr[n]; } virtual T* operator->() { return rptr; } }; // /////////////////////////////////////////////////////////////// // definitions of public member functions // /////////////////////////////////////////////////////////////// //----< point to top of allocated memory >--------------------- template inline void ptr::first() { top ? rptr = top->array : 0; } //----< return size of allocated memory >---------------------- template inline int ptr::size() const { return top ? top->_max : 0; } //----< request reallocation of this object's memory block >--- template int ptr::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 ptr::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 ptr()"); 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 ptr::ptr(const T* t, int size) { alloc(size); int i; if(size >0) for(i=0; iarray)[i] = t[i]; } //----< construct array >-------------------------------------- template ptr::ptr(int size) { alloc(size); } //----< copy constructor >------------------------------------- template ptr::ptr(const ptr &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 ptr::~ptr() { if(top) top->delRef(); #ifdef TEST_SPTRS if(_showAlloc) cout << " " << bytes_alloc << " remaining ptr bytes allocated\n"; #endif } // //----< assignment operator >---------------------------------- template ptr& ptr::operator=(const ptr &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 ptr& ptr::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& ptr::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 ptr::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& ptr::operator*() { return (this->operator[](0)); } //----< additive assignment >---------------------------------- template ptr& ptr::operator+=(int n) { rptr+=n; return *this; } //----< addition of integer offset >--------------------------- template ptr ptr::operator+(int n) { ptr sum = *this; return(sum += n); } // //----< subtractive assignment >------------------------------- template ptr& ptr::operator-=(int n) { rptr-=n; return *this; } //----< subtraction of integer offset >------------------------ template ptr ptr::operator-(int n) { ptr diff = *this; return (diff-=n); } //----< preincrement operator >-------------------------------- template ptr& ptr::operator++() { rptr++; return *this; } //----< postincrement operator >------------------------------- template ptr ptr::operator++(int) { ptr old = *this; rptr++; return old; } //----< predecrement operator >-------------------------------- template ptr& ptr::operator--() { rptr--; return *this; } //----< postdecrement operator >------------------------------- template ptr ptr::operator--(int) { ptr old = *this; rptr--; return old; } //----< is null? >--------------------------------------------- template int ptr::operator!() { return ((rptr == 0) ? 1 : 0); } //----< member selection operator >---------------------------- template T* sptr::operator->() { if(rptr == 0) { cout << "selecting with invalid pointer in operator->()\n"; exit(1); } return rptr; } // //----< display smart pointer state >-------------------------- template void ptr::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