Reference counting reminds me of Java. If you want to use C ++ to implement Java capabilities, Reference counting is indispensable. Reference counting can save program operation costs, and the cost of a large number of constructor, destructor, allocation, release, and copy is omitted.
Implementation
ClassRCObject { Public: RCObject (): refCount (0), retriable (true ){} RCObject (constRCObject &): refCount (0), retriable (true ){} RCObject & operator = (constRCObject & rhs) {return * this ;} Virtual ~ RCObject () = 0; Void AddReference () {++ refCount ;} Void RemoveReference () {if (-- refCount = 0) deletethis ;}
Void markunretriable () {retriable = false ;} Bool isShareable () const {returnshareable ;} Bool isShared () const {returnrefCount> 1 ;} Private: Int refCount; Bool retriable; }; RCObject ::~ RCObject (){}
Template <classT> Class RCPtr { Public: RCPtr (T * realPtr = 0): pointee (realPtr) {init ();} RCPtr (constRCPtr & rhs): pointee (rhs. pointee) {init ();} ~ RCPtr () {if (pointee) pointee-> RemoveReference ();} RCPtr & operator = (constRCPtr & rhs) { If (pointee! = Rhs. pointee) { If (pointee) Pointee-> RemoveReference (); Pointee = rhs. pointee; Init (); } Return * this; } T * operator-> () const {returnpointee ;} T & operator * () const {return * pointee ;} Private: T * pointee; Void init () { If (pointee = 0) Return; If (pointee-> isShareable () = false) Pointee = newT (* pointee ); Pointee-> AddReference (); } };
Class String { Public: String (const char * value = ""): value (newStringValue (value )){} Const char & operator [] (intnIndex) const { Return value-> data [nIndex]; } Char & operator [] (intnIndex) { If (value-> isShared ()) Value = newStringValue (value-> data ); Value-> markunretriable (); Returnvalue-> data [nIndex]; } Protected: Private: Struct StringValue: publicRCObject { Char * data; String Value (constchar * initValue) { Init (initValue ); } String Value (constStringValue & rhs) { Init (rhs. data ); } Void init (constchar * initValue) { Data = newchar [strlen (initValue) + 1]; Strcpy (data, initValue ); } ~ String Value () { Delete [] data; } }; RCPtr <StringValue> value; }; |
This is the String implementation provided by Meyers. However, my opinion is that if there is no special need, it is best not to use reference count for stirng, because the cost of synchronization in a multi-threaded program is greater than the benefit of the reference count itself, it is not worth the candle.
What if StringValue is a ready-made class and cannot be modified? You can use delegation. The following is a typical implementation:
ClassRCObject { Public: RCObject (): refCount (0), retriable (true ){} RCObject (constRCObject &): refCount (0), retriable (true ){} RCObject & operator = (constRCObject & rhs) {return * this ;} Virtual ~ RCObject () = 0; Void AddReference () {++ refCount ;} Void RemoveReference () {if (-- refCount = 0) deletethis ;}
Void markunretriable () {retriable = false ;} Bool isShareable () const {returnshareable ;} Bool isShared () const {returnrefCount> 1 ;} Private: Int refCount; Bool retriable; }; RCObject ::~ RCObject (){}
Template <classT> Class RCIPtr { Public: RCIPtr (T * realPtr = 0): counter (new CountHolder) { Counter-> pointee = realPtr; Init (); } RCIPtr (constRCIPtr & rhs): counter (rhs. counter) { Init (); } ~ RCIPtr () { Counter-> RemoveReference (); } RCIPtr & operator = (constRCIPtr & rhs) { If (counter! = Rhs. counter) { Counter-> RemoveReference (); Counter = rhs. counter; Init (); } Return * this; } ConstT * operator-> () const { Returncounter-> pointee; } T * operator-> () { MakeCopy (); Returncounter-> pointee; } ConstT & operator * () const { Return * (counter-> pointee ); } T & operator *() { MakeCopy (); Return * (counter-> pointee ); } Private: Struct CountHolder: publicRCObject { ~ Count Holder () {deletepointee ;} T * pointee; }; Count Holder * counter; Void init () { If (counter-> isShareable () = false) { T * oldValue = counter-> pointee; Counter = newCountHolder; Counter-> pointee = newT (* oldValue ); } Counter-> AddReference (); } Void makeCopy () { If (counter-> isShared ()) { T * oldValue = counter-> pointee; Counter-> RemoveReference (); Counter = newCountHolder; Counter-> pointee = newT (* oldValue ); Counter-> AddReference (); } } }; Class Widget { Public: Widget (intSize ){} Widget (constWidget & rhs ){} ~ Widget (){} Widget operator = (const Widget & rhs ){} Void doThis () {printf ("doThis ()"); return ;} Int showThat () const {printf ("showThat ()"); return 0 ;} Protected: Private: Inti; };
Class RCWidget { Public: RCWidget (intsize): value (newWidget (size )){} Void doThis () {value-> doThis ();} Int showThat () const {returnvalue-> showThat ();} Protected: Private: RCIPtr <Widget> value; }; |
Evaluation
It is necessary to implement reference counting. In not all cases, it is appropriate to use reference counting. The applicable situations are as follows:
A relatively large number of objects share a relatively small amount of real values.
The cost of generating or destroying the object's real value is very high, or it occupies a lot of memory.
But remember that even Java will have a memory leak. Don't expect a small reference count (simple implementation above) to avoid the same problem.
Reference counting is a very profound technique. Think about Java, so you need to be cautious. I hope it will bring optimization in programming.