100 likes | 209 Views
Reference counting, handles and the STL. Fred Kuhns fredk@cse.wustl.edu Applied Research Laboratory, Department of Computer Science and Engineering, Washington University in St. Louis. Background.
E N D
Reference counting, handles and the STL Fred Kuhns fredk@cse.wustl.edu Applied Research Laboratory, Department of Computer Science and Engineering, Washington University in St. Louis
Background • In order to take advantage of polymorphism in C++ you must use either pointers or references. • The question of object ownership is critical • Containers own the objects they contain: • copy is made on insertion • object deleted when removed or container deleted • Often you will use Object pointers with STL containers • Consequences: • could make a new object each time it is referenced • make one object, then copy its pointer CSE332: Object Oriented Programming Laboratory
Issues • Object use issues: • is this a const object (all users need read-only access). Then multiple copes or one copy and multiple references is fine • if users alter object then what are the semantics? Should each user have their own copy or is one global object required. • if object is not changed frequently then copy-on-write semantics is viable • efficiency issues: • many copies of an object • creates time and space overhead • how to keep all copies consistent (or knowing if you need to) • One copy with many references: • concurrency control issues • knowing when an object is no longer needed CSE332: Object Oriented Programming Laboratory
Problems and Solutions • context: class hierarchy captures behavioral differences • problem: container is only able to store objects of a specific type • solution: store object references or addresses in containers • context: containers own contained objects • problem: objects are copied causing efficiency problems • solution: store object pointers in containers • context: object pointer appears in many containers. • problem: how do you know when it is safe to delete object • solution: reference counting • context: many references to an object that is infrequently changed. • problem: changes should only be visible to user making the change. • solution: copy-on-write semantics CSE332: Object Oriented Programming Laboratory
A more general observation • “Counted Pointer Idiom”, J.O. Coplien, Advanced C++ - Programming Styles and Idioms • context: memory management of dynamically created objects • pointers are convenient for dynamic structures (trees etc) and for sharing • passing object pointers is efficient • problem: managing object pointers is error prone. • Solution: known by many names - Counted Pointer Idiom (aka Handler idiom); proxy pattern; smart pointers; Handle-Body idiom • Handler can be passed by value • Pointer may be assigned to Handler so when you leave the enclosing scope Handler is destroyed CSE332: Object Oriented Programming Laboratory
Reference Counting Techniques (intrusive) class Handle { public: Handle(…) { body = new Body(…); body->refCount++; } Handle(const Handle& h) { body = h.body; body->refCount++; } Handle& operator=(const Handle& h) { h.body->refCount++; if (--body->refCount <= 0) delete body; body = h.body; return *this; } ~Handle() { if (--body->refCount <= 0) delete body; } Body* operator->() {return body;} private: Body *body; }; class Body { public: void service(); private: friend class Handle; Body( … ) { … } ~Body( … ) { … } int refCount; }; CSE332: Object Oriented Programming Laboratory
Non-Intrusive Example template <class X> class Handle { X* rep; int* pcount; public: Handle() : rep(new X), pcount(new int(1)){} Handle(X* pp): rep(pp), pcount(new int(1)){} Handle(const Handle& r) : rep(r.rep), pcount(r.pcount){*pcount)++;} Handle<X>& operator=(const Handle<X>& r) { if (rep == r.rep) return *this; if(--(*pcount) == 0) { delete rep; delete pcount; } rep = r.rep; pcount = r.pcount; (*pcount)++; return *this; } ~Handle() { if (--(*pcount) == 0) {delete rep; delete pcount;} } X* operator->() {return rep;} }; Can you think of other operators for handler? CSE332: Object Oriented Programming Laboratory
Do a little more • If you need to perform some extra processing on the Body object you may replicate the representation classes interface within the Handle class. • For example, in a multithreaded program (or multi-process with shared memory) you can add locking primitives to the appropriate method calls or member object references. template <class T> class ImplHandler { T* rep; Lock lock; int *pcount; public: void value() {Lock_ptr x(lock); return rep->value();} … }; CSE332: Object Oriented Programming Laboratory
Copy-On-Write Semantics • Use a Handle to reference object • if const object returned then OK • if not const then need to know if user is/will write to the object • overload any and all body member functions that alter the object’s state. operator=() and operator*() are likely candidates CSE332: Object Oriented Programming Laboratory
String Class Example • class String { • private: • class StrRep { • // define how string is stored and managed • … }; • StrRep* *srep_; • public: • class Cref { • friend class String; String& s_; int i_; • Cref(String &s, int i) : s_(s), i_(i) {} • public: • operator char() {return s_.read(i_);} // type conversion • void operator=(char c) {s_.write(i_, c);} // assignment • }; • String& operator=(const String& s) { • s.srep_->ref_++; • if (--srep_->ref_ == 0) delete srep_; • srep_ = s.srep_; return *this; • } • char read(int i) const {return srep_->str_[i];} • void write(int i, char c) • {srep_ = srep_->get_copy(); srep_->str_[i] = c;} • char operator[](int i) const {return srep_->str_[i];} • Cref operator[](int i) {return Cref(*this, 0);} • … }; CSE332: Object Oriented Programming Laboratory