count class instances for every derived class

前端 未结 7 2094
栀梦
栀梦 2021-02-19 09:09

is there any way to make all derived classes count their instances?How (write code in one of C++, C#, Java)?

Imagine I\'ve got access to the root class (e.g. object), an

相关标签:
7条回答
  • 2021-02-19 09:25

    It strikes me that if you if you actually want the injected/whatever class to actually do something useful then you must couple it in some way to the original classes, whether it's by inheritance or direct coupling with method calls. Otherwise you've just got two wheels spinning independently.

    The only alternative I can think of is to use a factory pattern that can count creations for you -- but you'd have to hack something to count decrements, like handing the object back to the factory explicitly.

    0 讨论(0)
  • 2021-02-19 09:29

    In .Net, generics could be used to accomplish this. The following technique will not work in Java due to type erasure.

    public static class InstanceCounter<T>
    {
        private static int _counter;
    
        public static int Count { get { return _counter; }}
    
        public static void Increase()
        {
            _counter++;
        }
    
        public static void Decrease()
        {
            _counter--;
        }
    }
    

    Now in your classes, be it base or subclasses, use it as follows:

    public class SomeClass
    {
        public SomeClass()
        {
            InstanceCounter<SomeClass>.Increase();        
        }
    
        ~SomeClass()
        {
            InstanceCounter<SomeClass>.Decrease();
        }
    }
    

    You don't have to include the instance count property in every class either, it is only needed on the InstanceCounter class.

    int someClassCount = InstanceCounter<SomeClass>.Count;
    

    Note: this sample does not require classes to inherit the instance counter class.

    If one can afford to burn the one-superclass restriction in .Net, the following would also work:

    public class InstanceCounter<T>
    {
        private static int _counter;
    
        public static int Count { get { return _counter; }}
    
        protected InstanceCounter<T>()
        {
            _counter++;
        }
    
        ~InstanceCounter<T>()
        {
            _counter--;
        }
    }
    
    public class SomeClass : InstanceCounter<SomeClass>
    {
    }
    

    Then retrieving the count:

    int someClassCount = InstanceCounter<SomeClass>.Count;
    

    or

    int someClassCount = SomeClass.Count;
    

    Note2: As mentioned in the comments, using the finalizer (~SomeClass) is slow, and will only decrease the counter when the instance is actually collected by the GC. To get around this one would have to introduce deterministic "freeing" of instances, e.g. implementing IDisposable.

    0 讨论(0)
  • 2021-02-19 09:31

    In C++, you can do it with a template base class. Basically it is a mixin, so it does still require each class to co-operate by inheriting from the mixin:

    // warning: not thread-safe
    template <typename T>
    class instance_counter {
      public:
        static size_t InstancesCount() { return count(); }
        instance_counter() { count() += 1; }
        instance_counter(const instance_counter&) { count() += 1; }
        // rare case where we don't need to implement the copy assignment operator.
      protected:
        ~instance_counter() { count() -= 1; }
      private:
        static size_t &count {
            static size_t counter = 0;
            return counter;
        }
    };
    
    class my_class: public instance_counter<my_class> {};
    

    Since each class using the template has a different base class, it has a different count function and hence a different copy of the static variable counter.

    The trick of inheriting from a template class that's instantiated using the derived class as a template parameter is called CRTP.

    0 讨论(0)
  • 2021-02-19 09:34

    In Java you can use a global Multiset:

    import com.google.common.collect.ConcurrentHashMultiset;
    
    public abstract class InstanceCounted {
    
        protected InstanceCounted() {
            COUNT_MAP.add(this.getClass());
        }
    
        protected static final ConcurrentHashMultiset<Class<? extends InstanceCounted>> COUNT_MAP =
            ConcurrentHashMultiset.create();
    
    }
    

    Alternatively you can use a Map<Class, Integer> if you don't want the dependency on guava.

    Note: this only tracks instance creation, not garbage collection, so the count will never decrease. You can also track collection using PhantomReferences if you are willing to take a performance hit:

    import java.lang.ref.PhantomReference;
    import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    
    import com.google.common.collect.HashMultimap;
    import com.google.common.collect.Multimap;
    import com.google.common.collect.Multimaps;
    
    public abstract class InstanceCounted {
    
        public static int getInstanceCount(Class<? extends InstanceCounted> clazz) {
            reap();
            return INSTANCES.get(clazz).size();
        }
    
        protected InstanceCounted() {
            reap();
            INSTANCES.put(getClass(), new CountingReference(this));
        }
    
        static final Multimap<Class<? extends InstanceCounted>, CountingReference> INSTANCES =
            Multimaps.synchronizedSetMultimap(HashMultimap.<Class<? extends InstanceCounted>, CountingReference>create());
    
        static final ReferenceQueue<InstanceCounted> QUEUE =
            new ReferenceQueue<InstanceCounted>();
    
        private static void reap() {
            Reference<? extends InstanceCounted> ref;
            while ((ref = QUEUE.poll()) != null) {
                ((CountingReference) ref).clear();
            }
        }
    
        private static class CountingReference extends PhantomReference<InstanceCounted> {
    
            public void clear() {
                super.clear();
                INSTANCES.remove(clazz, this);
            }
    
            CountingReference(InstanceCounted instance) {
                super(instance, QUEUE);
                this.clazz = instance.getClass();
            }
    
            private final Class<? extends InstanceCounted> clazz;
    
        }
    
    }
    
    0 讨论(0)
  • 2021-02-19 09:40

    In Java you can implement the counting function to the common super class of your hirachy.

    This base class contains a Map - associating the classes to the number of instances. If a instance of base or one of its sub class is created, then the constructor ins invoked. The constructor increase the number of instances of the concreate class.

    import java.util.Map.Entry;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class Base {
    
        /** Threadsave counter */
        private static final ConcurrentMap<Class<? extends Base>, AtomicInteger>
           instancesByClass 
           = new ConcurrentHashMap<Class<? extends Base>, AtomicInteger>(
                10);
    
        /** The only one constructor of base */
        public Base() {
            Class<? extends Base> concreateClass = this.getClass();
            AtomicInteger oldValue = instancesByClass.putIfAbsent(concreateClass,
                    new AtomicInteger(1));
            if (oldValue != null) {
                oldValue.incrementAndGet();
            }
        }
    
        /* DEMO starts here */
        public static class SubA extends Base{
        }
    
        public static class SubB extends Base{
        }
    
        public static class SubSubA extends SubA{
        }
    
    
        public static void main(String[] args) {
            printNumbers();
            new SubA();
            new SubA();
    
            new SubB();
    
            new SubSubA();
    
            printNumbers();
        }
    
        private static void printNumbers() {
            // not thread save!
            for (Entry<Class<? extends Base>, AtomicInteger> item : instancesByClass
                    .entrySet()) {
                System.out.println(item.getKey().getName() + "  :  "
                        + item.getValue());
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-19 09:46

    I would use a template. This is in C++, by the way.

    template<typename T> class object {
    private:
        static int count;
    public:
        object() { count++; }
        object(const object&) { count++; }
        ~object() { count--; }
        static int GetCount() { return count; }
    };
    template<typename T> int object<T>::count = 0;
    

    RTTI solution:

    class object {
        static std::map<std::string, int> counts;
    public:
        object() { counts[typeid(*this).name()]++; }
        object(const object&) { counts[typeid(*this).name()]++; }
        ~object() { counts[typeid(*this).name()]--; }
        template<typename T> int GetObjectsOfType() {
            return counts[typeid(T).name()];
        }
        int GetObjectsOfType(std::string type) {
            return counts[type];
        }
    };
    std::map<std::string, int> object::counts;
    

    RTTI is less invasive and allows run-time selection of the type to be queried, but the template has far less overhead and you can use it to count every derived class individually, whereas RTTI can only count the most derived classes individually.

    0 讨论(0)
提交回复
热议问题