home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!zaphod.mps.ohio-state.edu!howland.reston.ans.net!usc!cs.utexas.edu!ut-emx!jamshid
- From: jamshid@ut-emx.uucp (Jamshid Afshar)
- Newsgroups: comp.lang.c++
- Subject: Re: static "inheritance" question
- Summary: I think I have the solution (actually, everybody seemed to have part of it)
- Message-ID: <86431@ut-emx.uucp>
- Date: 11 Jan 93 01:35:51 GMT
- References: <1993Jan4.144320.5586@dayfac.cdc.com>
- Reply-To: jamshid@emx.utexas.edu
- Organization: The University of Texas at Austin; Austin, Texas
- Lines: 299
-
- I quote a few different articles below -- watch the attrib lines.
-
- In article <1993Jan4.144320.5586@dayfac.cdc.com> pault@dayfac.cdc.com (Paul Thompson;DAYFAC-ITS;) writes:
- >Consider class A which counts its 'live' instantiations.
- >
- > class A {
- > static int nr;
- > public:
- > A() {nr++;}
- > ~A() {nr--;}
- > int getNrInstances () {return nr;}
- > };
- > ...
- > A::nr = 0;
-
- Note that this code would not keep an accurate count since A doesn't
- also define a copy-ctor which incremements 'nr'. I realize it's just
- sample code, but forgetting about the compiler-generated copy-ctor in
- situations like this is a common error.
-
- Paul continues:
- >[...]
- >Could anyone enlighten me as to how to acquire the functionality
- >of a class like A that depends on a static variable with LEAST
- >modification to the acquiring class ? Thanks.
-
- maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) answers in article <1993Jan5.051940.28930@ucc.su.OZ.AU>
- | IF: you want to count all objects allocated (of any type):
- |
- | Sure: two type of class: base classes and working
- |classes. The base classes NEVER inherit A.
- |The working classes inherit from A when you want a counter.
- |It is not allowed to derive from a working class though.
- |[code deleted]
-
- Not allowing one to derive from the "working" class is too serious a
- restriction, IMHO.
-
- John continues:
- |IF: you want to partition the counts, you can do this:
- |
- | class C1 {}; class C1 {}; class C3 {};
- |
- |and now make 'A' a template class and use A<C1>, A<C2> or A<C3>
- |as appropriate.
-
- I'm not sure what you mean here. Are the Cs supposed to inherit from
- A<Cx>?
-
- John continues:
- |IF: you want to count instances of EACH class, you must
- |declare the static variable for each class .. no getting
- |around this. However, you CAN automate the increment
- |and decrement somewhat:
- |
- | class Counter {
- | virtual int* counter()=0;
- | public:
- | Counter() { (*counter())++; }
- | ~Counter() { (*counter())--; }
- | int TellCount()const {return *counter();}
- | };
- |[...]
- |#define autocount public virtual Counter { static ....
- | class X : autocount
- | ...
- | };
- |
- |(Yuk)
- |(PS: I havent tested this)
-
- Famous last words :-). I do agree it would be a yucky solution, but
- fortunately it just doesn't work. The Counter ctor/dtor are calling
- a virtual function (and a pure one at that) -- make sure your
- compiler sternly warns about this.
-
- I think I have a pretty good solution -- it only requires the class
- derive from (or contain a) KeepCount<T>, and it is safe. John was
- on the right track making the counter class a template. I'm going to
- walk through the steps I took to reach the solution since I found it
- to be a very interesting problem to solve. First, I started with a
- KeepCount class similar to A above. Any class wanting a "live count"
- would derive from KeepCount. Since each class should have its own
- count, KeepCount must be a class template. With a little work, you
- could probably simulate KeepCount using <generic.h> if your compiler
- doesn't implement templates yet.
-
- template<class T>
- class KeepCount {
- static unsigned n; // each 'T' has its own 'n'
- protected: // KeepCount only useful as base class
- KeepCount() { ++n; }
- KeepCount(const KeepCount&) { ++n; }
- ~KeepCount() { --n; }
- public:
- static unsigned count() { return n; }
- };
- template<class T> unsigned KeyCount<T>::n = 0; // in KeyCount.cc
-
- class A : public KeyCount<A> {/*...*/};
-
- "A::count()" would then give you the current number of live A objects
- [btw, I just noticed Nikke Locke posted this same solution]. But, I
- think a major problem with this solution is when you derive from A.
-
- class B : public A {/*...*/};
-
- "B::count()" would actually return the total number of A objects. Even
- if B kept its own count by inheriting from KeepCount<B>, B must
- provide its own definition of "count()" since "B::count()" would
- otherwise be ambiguous:
-
- class B : public A, public KeyCount<B> {
- public:
- static unsigned count() { return KeyCount<B>::count(); } // tedious
- };
-
- The key to solving this problem is to use KeyCount<X>::count()
- instead of X::count() [and I just noticed that Howard Ferguson wrote
- pretty much the same thing in <1ikeekINN1sb@news.cerf.net>]. We
- could even make X privately inherit from KeyCount<X> to make sure
- noone accidentally uses X::count(). There's still one problem with
- this, though -- there's no safeguard verifying the class X really did
- inherit from KeyCount<X>. KeyCount<X>::count() would just always
- return 0 if X didn't derive from KeyCount<X>. My first idea for
- solving this problem was:
-
- template<class T>
- class KeepCount {
- static unsigned n;
- // rest same as above
- public:
- static unsigned count() {
- return T::n; // error if T not derived from KeepCount
- }
- };
-
- The problem with this (otherwise elegant, IMO) solution is that
- "T::n" is ambigous in derived classes.
-
- class B : public A, public KeyCount<B> {
- };
- // B::n is ambigous (A's KeyCount<A>::n or KeyCount<B>::n?)
-
- Also, there's the possibility that the name 'n' would be defined by
- the class B. So, I decided to use the unqualified 'n' in count(), but
- build some sort of safeguard into the count() function so that it
- would give a compile-time error if the class forgot to inherit from
- KeyCount:
-
- template<class T>
- class KeepCount {
- //... same as above
- public:
- static unsigned count() {
- KeepCount* test = (T*)0; // make sure T derived from me
- return n;
- }
- };
- class A : KeepCount<A> {};
- // "KeepCount<A>::count()" returns number of live A objects
- class B : public A, KeepCount<B> {};
- // "KeepCount<B>::count()" returns number of live B objects
- class C : public A {};
- // "KeepCount<C>::count()" is compile-time error
-
- That's it. Comments? A couple of BC++ 3.1 problems: count() has to
- be defined outside the class and not inlined -- BC++ parses count()
- before its even used, and this causes a problem since the T is not
- fully defined. I believe Standard C++ will require the above code
- work as expected. Also, BC++ seems to allow "X* x = (Y*)0" even if Y
- is not derived from X. You have to add a dummy intermediate variable:
- T* hack=0; KeepCount* test = hack; // make sure T derived from KeepCount
- Finally, BC++ 3.1 doesn't actually give a compile-time error for
- count() in situations like "KeepCount<C>::count()" above. Instead it
- seems to simply ignore the template function definition and gives a
- link error that KeepCount<C>::count was not defined.
-
- John writes in another article:
- |You would have to do something like:
- |
- | class B : public virtual A<B> { ...
- |
- |which seems dubious. Then the template class A<T> could create
- |the int A<T>::counter automagically. Dont know if this
- |works.
-
- No, that's fine -- you just provide a static member definition for
- 'counter' as you would for a non-template class. But, I see no
- reason to inherit virtually from A<B>. You won't be inheriting
- from another A<B> (unless it's through another B).
-
- In article <1ikeekINN1sb@news.cerf.net> hlf@nic.cerf.net (Howard Ferguson) writes:
- ]1. If you are going to inherit make A an abstract data type by
- ]making its destructor a pure virtual function (see Meyers Effective
- ]C+, item 14)
-
- You wouldn't want to make ~A() pure since it must do some work
- (decrement the "live" count). I don't see much reason to make it
- virtual since 'A*'s will not be used.
-
- ]2. You probably do not want to inherit anyway because inheriting
- ]means you want to model IS_A, and this is not the case.
-
- I don't think inheritance in this situation is a bad design. I can't
- see any strong reason to choose either private inheritance or
- containment. I would probably base it on compiler efficiency --
- would it still take up space for an empty member vs. an empty base
- class? I don't think C++ requires space for either, so even this is
- probably not a good determiner of which to use. But I will say that
- inhertance, like other C++ features, are often useful for non-OOD
- reasons.
-
- Working code is appended.
-
- Jamshid Afshar
- jamshid@emx.utexas.edu
-
- // compiles under BC++ 3.1; "##" comments mark BC++ bugs
-
- #include <assert.h>
-
- template<class T>
- class KeepCount {
- static unsigned n;
- protected:
- KeepCount() { ++n; }
- KeepCount(const KeepCount&) { ++n; }
- // define a copy-ctor in case a copy-ctor is generated in derived class
- ~KeepCount() { --n; }
- public:
- static unsigned count();
- };
-
- template<class T> /*##inline*/
- unsigned KeepCount<T>::count()
- {
- T* hack=0; KeepCount<T>* test = hack; //## "KeepCount* p = (T*)0;"
- //## should be enough
- (void)hack; // takes care of most compilers' "variable not used" msgs
- return KeepCount<T>::n;
- }
-
- template<class T>
- unsigned KeepCount<T>::n = 0;
-
- class A : KeepCount<A> {};
-
- class B : KeepCount<B> {};
-
- class C : public A {}; // woops, forgot to derive from KeepCount
-
- class D : public A, public B {};
-
- class E : public A, public B, KeepCount<E> {};
-
- main()
- {
- assert(KeepCount<A>::count()==0);
- assert(KeepCount<B>::count()==0);
- // assert(KeepCount<C>::count()==0); // give error (##at link time)
- // assert(KeepCount<D>::count()==0); // give error (##at link time)
- assert(KeepCount<E>::count()==0);
- {
- A a1; B b1;
- assert(KeepCount<A>::count()==1);
- assert(KeepCount<B>::count()==1);
- {
- A a2; B b2;
- assert(KeepCount<A>::count()==2);
- assert(KeepCount<B>::count()==2);
- {
- A a3 = a2; B b3 = b2;
- assert(KeepCount<A>::count()==3);
- assert(KeepCount<B>::count()==3);
- }
- assert(KeepCount<A>::count()==2);
- assert(KeepCount<B>::count()==2);
- }
- assert(KeepCount<A>::count()==1);
- assert(KeepCount<B>::count()==1);
- }
- {
- C c;
- assert(KeepCount<A>::count()==1);
- assert(KeepCount<B>::count()==0);
- D d;
- assert(KeepCount<A>::count()==2);
- assert(KeepCount<B>::count()==1);
- E e;
- assert(KeepCount<A>::count()==3);
- assert(KeepCount<B>::count()==2);
- assert(KeepCount<E>::count()==1);
- }
- assert(KeepCount<A>::count()==0);
- assert(KeepCount<B>::count()==0);
- assert(KeepCount<E>::count()==0);
- return 0;
- }
-