home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / lang / cplus / 12397 < prev    next >
Encoding:
Text File  |  1992-08-16  |  5.8 KB  |  183 lines

  1. Path: sparky!uunet!elroy.jpl.nasa.gov!sdd.hp.com!cs.utexas.edu!tamsun.tamu.edu!snorkelwacker.mit.edu!ai-lab!life.ai.mit.edu!tmb
  2. From: tmb@arolla.idiap.ch (Thomas M. Breuel)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Reference counting for vectors, (Tony Hansen's book).
  5. Message-ID: <TMB.92Aug16160535@arolla.idiap.ch>
  6. Date: 16 Aug 92 20:05:35 GMT
  7. References: <1992Aug14.164719.9719@wuecl.wustl.edu>
  8.     <BRISTER.92Aug14110706@tirade.decwrl.dec.com>
  9.     <TMB.92Aug14232208@arolla.idiap.ch>
  10.     <BRISTER.92Aug14170213@tirade.decwrl.dec.com>
  11. Sender: news@ai.mit.edu
  12. Reply-To: tmb@idiap.ch
  13. Organization: IDIAP (Institut Dalle Molle d'Intelligence Artificielle
  14.     Perceptive)
  15. Lines: 165
  16. In-reply-to: brister@decwrl.dec.com's message of 15 Aug 92 00:02:13 GMT
  17.  
  18. In article <BRISTER.92Aug14170213@tirade.decwrl.dec.com> brister@decwrl.dec.com (James Brister) writes:
  19.  
  20.    On 15 Aug 92 03:22:08 GMT, tmb@arolla.idiap.ch (Thomas M. Breuel) said:
  21.    > Using reference counting to get reference semantics on parameter
  22.    > passing is very confusing indeed (sadly, this bad idea seems to
  23.    > pervade the C++ literature).
  24.  
  25.    Perhaps if it's so pervasive then it's not such a bad idea.
  26.  
  27.    parameter passing was only a simple example, there are lots of cases where
  28.    multiple objects need to keep track of a piece of data, and unless you're
  29.    very sure about who is supposed to delete the data and when (non-trivial
  30.    when the system gets complex), you can get yourself into a lot of trouble.
  31.    Reference counting can save the day.
  32.  
  33. Memory management and reference/value semantics are very different
  34. concepts (though implementationally closely related).
  35.  
  36. From the user's point of view, there is no difference in terms of
  37. memory management between (correctly implemented) arrays with
  38. reference semantics or arrays with value semantics. You don't have to
  39. think about "who is supposed to delete the data" in either case.
  40.  
  41. In terms of behavior, there is, of course, a big difference.  Arrays
  42. with reference semantics mean that any name that has been initialized
  43. from, or assigned to, from some other such array object will be
  44. aliased to the same piece of memory. Unexpected aliasing teds to lead
  45. to difficult to trace bugs in programs.
  46.  
  47. Arrays with value semantics, on the other hand, behave just like
  48. scalars and structures, which is what programmers expect and are used
  49. to. The worst that can happen is that you incur a few redundant
  50. copies, and if those happen to make a difference in terms of
  51. performance (which is _very_ rare), you can find those places by
  52. profiling.
  53.  
  54. Personally, I would prefer to be able to disallow assignment
  55. ("operator=(A&)") and copy constructor ("A(A&)") for my array classes
  56. altogether. Copying is a potentially expensive operation, and hence
  57. the user should ask for it explicitly. Aliasing (as you get it with
  58. reference counting implementations of arrays) is a dangerous
  59. operation, and hence the user should also ask for it explicitly.
  60.  
  61. Unfortunately, C++ forces you to use the same function (the copy
  62. constructor) for passing parameters, returning values, and
  63. initialization of variables. I have always considered this a
  64. shortcoming, although I'm not sure that it is fixable.
  65.  
  66. You can get a simple template implementation of arrays with value
  67. semantics via anonymous FTP from maya.idiap.ch:pub/tmb/array.shar.
  68.  
  69.    > If you truly want reference counting semantics, you can easily derive
  70.    > it from objects with value semantics using a template class.
  71.  
  72. See below for an implementation. I wrote it mostly for fun. I find
  73. reference counted semantics pretty useless, so I haven't tested it
  74. much.
  75.  
  76.    > Copy-on-write is not really an option for classes like arrays that
  77.    > require high-performance updates. You don't want to pay the price for
  78.    > the memory fetch and conditional branch on each array store.
  79.  
  80.    If the array copies itself the first time it is written to, then you don't
  81.    loose anything except on the first access, and with large data it would be
  82.    better to have COW than to duplicate everything to protect against
  83.    unintended changes.
  84.  
  85. This is not true. In general, you have to check a flag on every (not
  86. just the first) modification to see whether the array has been copied.
  87.  
  88. As usual, you can take my advice or leave it. I have found arrays with
  89. reference semantics to be a bother, and I recommend that people stay
  90. away from them.
  91.  
  92. Whether you use copy-on-write to get value semantics or something else
  93. makes little difference, except that copy-on-write has _some_ overhead
  94. on every update. For numerical code, I have found that a "dumb"
  95. implementation of arrays with value semantics is the better choice.
  96.  
  97.                     Thomas.
  98.  
  99. ================================================================
  100.  
  101. Counted.h
  102.  
  103. #ifndef __COUNTED__
  104. #define __COUNTED__
  105.  
  106. template <class T>
  107. struct Counted {
  108.  private:
  109.     int *count;
  110.     T *object;
  111.     void drop() {
  112.         if(!count) return;
  113.         (*count)--;
  114.         if(*count<=0) {
  115.             delete count;
  116.             delete object;
  117.             count=0;
  118.             object=0;
  119.         }
  120.     }
  121.     void acquire() {
  122.         (*count)++;
  123.     }
  124.  public:
  125.     Counted() {
  126.         count=0;
  127.         object=0;
  128.     }
  129.     Counted(T *object):object(object) {
  130.         count = new int(1);
  131.     }
  132.     ~Counted() {
  133.         drop();
  134.     }
  135.     Counted(Counted &other) {
  136.         count=other.count;
  137.         object=other.object;
  138.         acquire();
  139.     }
  140.     Counted &operator=(Counted &other) {
  141.         drop();
  142.         count=other.count;
  143.         object=other.object;
  144.         acquire();
  145.         return *this;
  146.     }
  147.  
  148.     T &operator*() {return *object;}
  149.     T *operator->() {return object;}
  150.     operator T() {return *object;}
  151.     
  152.  
  153.     // do _not_ provide "operator T*()"
  154. };
  155.  
  156. #endif
  157.  
  158. ================================================================
  159.  
  160. A simple test program for the above:
  161.  
  162. extern "C" {
  163. #include <stdio.h>
  164. }
  165. #include "Counted.h"
  166.  
  167. struct A {
  168.     int val;
  169.     A() { printf("created A@%p\n",this); }
  170.     ~A() { printf("destroyed A@%p\n",this); }
  171. };
  172.  
  173. main() {
  174.     Counted<A> x(new A);
  175.     x->val = 42;
  176.     printf("x@%p\n",&(*x));
  177.     Counted<A> y(x);
  178.     Counted<A> z;
  179.     z=x;
  180.     printf("z@%p\n",&(*z));
  181.     printf("%d\n",z->val);
  182. }
  183.