开发者

count class instances for every derived class

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), and every other class is (directly or indirectly) derived from this class. What I want is:

AnyDerivedClass.InstancesCount()

Problem is, one must keep track of count in static variable, but it is not possible to "inject" static variable into derived class from base class, this holds only for member variables. That is, I have to write something like:

class object 
{ 
 private static int count = 0; 
 protected object() { ++count; }
 protected ~object() { --count; } 
 public static InstancesCount() { return count; } 
};

class derived : object 
{
 private static int count = 0;
 public derived() { ++count; }
 public ~derived() { --count; }
 public static InstancesCount() { return count; }
}

This functionality is obviously repeated, and I cannot put it in base class. Note there are 2 ways of calculating: that if there are 7 instances of class derived1 and 8 instances of class derived2, there are (a) 15 instances of object or (b) 0 instances of object. I don't care which one, because I can't do neither (using reasonably practical means, e.g. imagine 100 classes, half of which are in library I cannot modify).

Of course, in theory it is possible to create map of (some run-type type identifier) => int count, and use ugly, slow, (run-time type)-based approach (at least in C#, Java).

Of course, if I can modify derived classes, I can use copy-paste (awful), macro (yeah I know), mixins (not in these languages) etc. But that is still real ugly.

This is specific problem, but It happened to me several times, that I'd like to be able to "inject" static member into derived class to solve problem elegantly.

Help much appreciated.

EDIT: thanks for good answer, in C++ it is p开发者_C百科ossibly to use CRTP (Curiously recurring template pattern) as well, but not in C#/Java (no multiple inheritance). Of course, one must have access to derived classes and add this base class, so question remains (in case there is no other way, this looks best).

EDIT 2: looks impossible with current languages. static part of every class is not inheriting (and that's right), but there is no inheriting singleton associated with every class, so these kinds of problems cannot be solved so elegantly. To illustrate things, look at the following code: normal members and static members are current OOP languages feature, "singleton" (or whatever word would be) members are my suggestion/desire:

class Base
{
    static int sMemberBase;
    int memberBase;

    //my wish (note that virtual for methods is allowed!):
    singleton int singletonMemberBase;
};
class Derived : Base
{
    static int sMemberDerived;
    int memberDerived;

    //my wish (note that virtual for methods is allowed!):
    singleton int singletonMemberDerived;
};

//taken apart: (note: XYZStatic classes do not derive)
class Base { int memberBase; }
class BaseStatic { int sMemberBase; } BaseStaticInstance;
class Derived : Base { int memberDerived; }
class DerivedStatic { int sMemberDerived;  } BaseStaticInstance;
//note: Derived::sMemberBase is compile-time changed to Base::sMemberBase

//my wish: (note inheritance!)
class BaseSingleton { int singletonMemberBase; } BaseSingletonInstance;
class DerivedSingleton : BaseSingleton { int singletonMemberDerived; } DerivedSingletonInstance;

If anything like that would be present in language, solution for my question would be simple and elegant:

//with singleton members, I could write counter like this:
class object
{
    singleton int count;
    object() { ++count; }
    ~object() { --count; }
};


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.


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;

    }

}


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());
        }
    }
}


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.


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.


Way in C# that sprung in my mind imediately:

class A : IDisposable
{
    static Dictionary<Type, int> _typeCounts = new Dictionary<Type, int>();
    private bool _disposed = false;

    public static int GetCount<T>() where T:A
    {
        if (!_typeCounts.ContainsKey(typeof(T))) return 0;

        return _typeCounts[typeof(T)];
    }

    public A()
    {
        Increment();
    }

    private void Increment()
    {
        var type = this.GetType();
        if (!_typeCounts.ContainsKey(type)) _typeCounts[type] = 0;
        _typeCounts[type]++;
    }

    private void Decrement()
    {
        var type = this.GetType();
        _typeCounts[type]--;            
    }

    ~A()
    {
        if (!_disposed) Decrement();
    }

    public void Dispose()
    {
        _disposed = true;
        Decrement();
    }
}

class B : A
{

}

And how to use it:

        A a1 = new A();
        Console.WriteLine(A.GetCount<A>());
        A a2 = new A();
        Console.WriteLine(A.GetCount<A>());            

        using(B b1 = new B())
        {
            Console.WriteLine(B.GetCount<B>());
        }
        Console.WriteLine(B.GetCount<B>());

The output could be done differently maybe. And its not pure OOP, but nor are C++ or Java examples in this thread. But it doesnt require a bit of code in inheriting class.

And dont forget about correcly disposing your objects!!


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜