home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #1 / NN_1993_1.iso / spool / comp / lang / cplus / 18911 < prev    next >
Encoding:
Text File  |  1993-01-10  |  10.8 KB  |  312 lines

  1. Path: sparky!uunet!zaphod.mps.ohio-state.edu!howland.reston.ans.net!usc!cs.utexas.edu!ut-emx!jamshid
  2. From: jamshid@ut-emx.uucp (Jamshid Afshar)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: static "inheritance" question
  5. Summary: I think I have the solution (actually, everybody seemed to have part of it)
  6. Message-ID: <86431@ut-emx.uucp>
  7. Date: 11 Jan 93 01:35:51 GMT
  8. References: <1993Jan4.144320.5586@dayfac.cdc.com>
  9. Reply-To: jamshid@emx.utexas.edu
  10. Organization: The University of Texas at Austin; Austin, Texas
  11. Lines: 299
  12.  
  13. I quote a few different articles below -- watch the attrib lines.
  14.  
  15. In article <1993Jan4.144320.5586@dayfac.cdc.com> pault@dayfac.cdc.com (Paul Thompson;DAYFAC-ITS;) writes:
  16. >Consider class A which counts its 'live' instantiations.
  17. >            class A { 
  18. >                static int nr;
  19. >              public:
  20. >                A() {nr++;}
  21. >               ~A() {nr--;}
  22. >                int getNrInstances () {return nr;}
  23. >            };
  24. >            ...
  25. >            A::nr = 0;
  26.  
  27. Note that this code would not keep an accurate count since A doesn't
  28. also define a copy-ctor which incremements 'nr'.  I realize it's just
  29. sample code, but forgetting about the compiler-generated copy-ctor in
  30. situations like this is a common error.
  31.  
  32. Paul continues:
  33. >[...]
  34. >Could anyone enlighten me as to how to acquire the functionality
  35. >of a class like A that depends on a static variable with LEAST
  36. >modification to the acquiring class ? Thanks.
  37.  
  38. maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) answers in article <1993Jan5.051940.28930@ucc.su.OZ.AU>
  39. |    IF: you want to count all objects allocated (of any type):
  40. |
  41. |    Sure: two type of class: base classes and working 
  42. |classes. The base classes NEVER inherit A.
  43. |The working classes inherit from A when you want a counter.
  44. |It is not allowed to derive from a working class though.
  45. |[code deleted]
  46.  
  47. Not allowing one to derive from the "working" class is too serious a
  48. restriction, IMHO.
  49.  
  50. John continues:
  51. |IF: you want to partition the counts, you can do this:
  52. |
  53. |    class C1 {}; class C1 {}; class C3 {};
  54. |
  55. |and now make 'A' a template class and use A<C1>, A<C2> or A<C3>
  56. |as appropriate.
  57.  
  58. I'm not sure what you mean here.  Are the Cs supposed to inherit from
  59. A<Cx>?
  60.  
  61. John continues:
  62. |IF: you want to count instances of EACH class, you must
  63. |declare the static variable for each class .. no getting
  64. |around this. However, you CAN automate the increment
  65. |and decrement somewhat:
  66. |
  67. |    class Counter {
  68. |        virtual int* counter()=0;
  69. |    public:
  70. |        Counter() { (*counter())++; }
  71. |        ~Counter() { (*counter())--; }
  72. |        int TellCount()const {return *counter();}
  73. |    };
  74. |[...]
  75. |#define autocount public virtual Counter { static ....
  76. |    class X : autocount
  77. |    ...
  78. |    };
  79. |
  80. |(Yuk)
  81. |(PS: I havent tested this)
  82.  
  83. Famous last words :-).  I do agree it would be a yucky solution, but
  84. fortunately it just doesn't work.  The Counter ctor/dtor are calling
  85. a virtual function (and a pure one at that) -- make sure your
  86. compiler sternly warns about this.
  87.  
  88. I think I have a pretty good solution -- it only requires the class
  89. derive from (or contain a) KeepCount<T>, and it is safe.  John was
  90. on the right track making the counter class a template.  I'm going to
  91. walk through the steps I took to reach the solution since I found it
  92. to be a very interesting problem to solve.  First, I started with a
  93. KeepCount class similar to A above.  Any class wanting a "live count"
  94. would derive from KeepCount.  Since each class should have its own
  95. count, KeepCount must be a class template.  With a little work, you
  96. could probably simulate KeepCount using <generic.h> if your compiler
  97. doesn't implement templates yet.
  98.  
  99.       template<class T>
  100.       class KeepCount {
  101.          static unsigned n;   // each 'T' has its own 'n'
  102.       protected:  // KeepCount only useful as base class
  103.          KeepCount() { ++n; }
  104.          KeepCount(const KeepCount&) { ++n; }
  105.          ~KeepCount() { --n; }
  106.       public:
  107.          static unsigned count() { return n; }
  108.       };
  109.       template<class T> unsigned KeyCount<T>::n = 0; // in KeyCount.cc
  110.  
  111.       class A : public KeyCount<A> {/*...*/};
  112.  
  113. "A::count()" would then give you the current number of live A objects
  114. [btw, I just noticed Nikke Locke posted this same solution].  But, I
  115. think a major problem with this solution is when you derive from A.
  116.  
  117.       class B : public A {/*...*/};
  118.  
  119. "B::count()" would actually return the total number of A objects. Even
  120. if B kept its own count by inheriting from KeepCount<B>, B must
  121. provide its own definition of "count()" since "B::count()" would
  122. otherwise be ambiguous:
  123.  
  124.       class B : public A, public KeyCount<B> {
  125.       public:
  126.          static unsigned count() { return KeyCount<B>::count(); } // tedious
  127.       };
  128.  
  129. The key to solving this problem is to use KeyCount<X>::count()
  130. instead of X::count() [and I just noticed that Howard Ferguson wrote
  131. pretty much the same thing in <1ikeekINN1sb@news.cerf.net>].  We
  132. could even make X privately inherit from KeyCount<X> to make sure
  133. noone accidentally uses X::count().  There's still one problem with
  134. this, though -- there's no safeguard verifying the class X really did
  135. inherit from KeyCount<X>. KeyCount<X>::count() would just always
  136. return 0 if X didn't derive from KeyCount<X>.  My first idea for
  137. solving this problem was:
  138.  
  139.       template<class T>
  140.       class KeepCount {
  141.          static unsigned n;
  142.          // rest same as above
  143.       public:
  144.          static unsigned count() {
  145.             return T::n;   // error if T not derived from KeepCount
  146.             }
  147.       };
  148.  
  149. The problem with this (otherwise elegant, IMO) solution is that
  150. "T::n" is ambigous in derived classes.
  151.  
  152.       class B : public A, public KeyCount<B> {
  153.       };
  154.       // B::n is ambigous (A's KeyCount<A>::n or KeyCount<B>::n?)
  155.  
  156. Also, there's the possibility that the name 'n' would be defined by
  157. the class B.  So, I decided to use the unqualified 'n' in count(), but
  158. build some sort of safeguard into the count() function so that it
  159. would give a compile-time error if the class forgot to inherit from
  160. KeyCount:
  161.  
  162.       template<class T>
  163.       class KeepCount {
  164.          //... same as above
  165.       public:
  166.          static unsigned count() {
  167.             KeepCount* test = (T*)0;  // make sure T derived from me
  168.             return n;
  169.             }
  170.       };
  171.       class A : KeepCount<A> {};
  172.       // "KeepCount<A>::count()" returns number of live A objects
  173.       class B : public A, KeepCount<B> {};
  174.       // "KeepCount<B>::count()" returns number of live B objects
  175.       class C : public A {};
  176.       // "KeepCount<C>::count()" is compile-time error
  177.  
  178. That's it.  Comments?  A couple of BC++ 3.1 problems: count() has to
  179. be defined outside the class and not inlined -- BC++ parses count()
  180. before its even used, and this causes a problem since the T is not
  181. fully defined.  I believe Standard C++ will require the above code
  182. work as expected.  Also, BC++ seems to allow "X* x = (Y*)0" even if Y
  183. is not derived from X.  You have to add a dummy intermediate variable:
  184.       T* hack=0; KeepCount* test = hack; // make sure T derived from KeepCount
  185. Finally, BC++ 3.1 doesn't actually give a compile-time error for
  186. count() in situations like "KeepCount<C>::count()" above.  Instead it
  187. seems to simply ignore the template function definition and gives a
  188. link error that KeepCount<C>::count was not defined.
  189.  
  190. John writes in another article:
  191. |You would have to do something like:
  192. |
  193. |    class B : public virtual A<B> { ...
  194. |
  195. |which seems dubious. Then the template class A<T> could create
  196. |the int A<T>::counter automagically. Dont know if this
  197. |works.
  198.  
  199. No, that's fine -- you just provide a static member definition for
  200. 'counter' as you would for a non-template class.  But, I see no
  201. reason to inherit virtually from A<B>.  You won't be inheriting
  202. from another A<B> (unless it's through another B).
  203.  
  204. In article <1ikeekINN1sb@news.cerf.net> hlf@nic.cerf.net (Howard Ferguson) writes:
  205. ]1. If you are going to inherit make A an abstract data type by
  206. ]making its destructor a pure virtual function (see Meyers Effective
  207. ]C+, item 14)
  208.  
  209. You wouldn't want to make ~A() pure since it must do some work
  210. (decrement the "live" count).  I don't see much reason to make it
  211. virtual since 'A*'s will not be used.
  212.  
  213. ]2.  You probably do not want to inherit anyway because inheriting 
  214. ]means you want to model IS_A, and this is not the case.
  215.  
  216. I don't think inheritance in this situation is a bad design.  I can't
  217. see any strong reason to choose either private inheritance or
  218. containment.  I would probably base it on compiler efficiency --
  219. would it still take up space for an empty member vs. an empty base
  220. class?  I don't think C++ requires space for either, so even this is
  221. probably not a good determiner of which to use.  But I will say that
  222. inhertance, like other C++ features, are often useful for non-OOD
  223. reasons.
  224.  
  225. Working code is appended.
  226.  
  227. Jamshid Afshar
  228. jamshid@emx.utexas.edu
  229.  
  230. // compiles under BC++ 3.1; "##" comments mark BC++ bugs
  231.  
  232. #include <assert.h>
  233.  
  234. template<class T>
  235. class KeepCount {
  236.    static unsigned n;
  237. protected:
  238.    KeepCount() { ++n; }
  239.    KeepCount(const KeepCount&) { ++n; }
  240.    // define a copy-ctor in case a copy-ctor is generated in derived class
  241.    ~KeepCount() { --n; }
  242. public:
  243.    static unsigned count();
  244. };
  245.  
  246. template<class T> /*##inline*/
  247. unsigned KeepCount<T>::count()
  248. {
  249.    T* hack=0; KeepCount<T>* test = hack;  //## "KeepCount* p = (T*)0;"
  250.                                           //## should be enough
  251.    (void)hack;  // takes care of most compilers' "variable not used" msgs
  252.    return KeepCount<T>::n;
  253. }
  254.  
  255. template<class T>
  256. unsigned KeepCount<T>::n = 0;
  257.  
  258. class A : KeepCount<A> {};
  259.  
  260. class B : KeepCount<B> {};
  261.  
  262. class C : public A {};     // woops, forgot to derive from KeepCount
  263.  
  264. class D : public A, public B {};
  265.  
  266. class E : public A, public B, KeepCount<E> {};
  267.  
  268. main()
  269. {
  270.    assert(KeepCount<A>::count()==0);
  271.    assert(KeepCount<B>::count()==0);
  272. //   assert(KeepCount<C>::count()==0);   // give error (##at link time)
  273. //   assert(KeepCount<D>::count()==0);   // give error (##at link time)
  274.    assert(KeepCount<E>::count()==0);
  275.    {
  276.       A a1; B b1;
  277.       assert(KeepCount<A>::count()==1);
  278.       assert(KeepCount<B>::count()==1);
  279.       {
  280.          A a2; B b2;
  281.          assert(KeepCount<A>::count()==2);
  282.          assert(KeepCount<B>::count()==2);
  283.          {
  284.             A a3 = a2; B b3 = b2;
  285.             assert(KeepCount<A>::count()==3);
  286.             assert(KeepCount<B>::count()==3);
  287.          }
  288.          assert(KeepCount<A>::count()==2);
  289.          assert(KeepCount<B>::count()==2);
  290.       }
  291.       assert(KeepCount<A>::count()==1);
  292.       assert(KeepCount<B>::count()==1);
  293.    }
  294.    {
  295.       C c;
  296.       assert(KeepCount<A>::count()==1);
  297.       assert(KeepCount<B>::count()==0);
  298.       D d;
  299.       assert(KeepCount<A>::count()==2);
  300.       assert(KeepCount<B>::count()==1);
  301.       E e;
  302.       assert(KeepCount<A>::count()==3);
  303.       assert(KeepCount<B>::count()==2);
  304.       assert(KeepCount<E>::count()==1);
  305.    }
  306.    assert(KeepCount<A>::count()==0);
  307.    assert(KeepCount<B>::count()==0);
  308.    assert(KeepCount<E>::count()==0);
  309.    return 0;
  310. }
  311.