计算每个派生类的类实例

时间:2011-01-10 13:22:38

标签: c# java c++ oop inheritance

有没有办法让所有派生类都计算它们的实例?如何(用C ++,C#,Java之一编写代码)?

想象一下,我可以访问根类(例如对象),并且每个其他类(直接或间接)派生​​自此类。我想要的是:

AnyDerivedClass.InstancesCount()

问题是,必须跟踪静态变量中的计数,但是不可能将静态变量“注入”到基类的派生类中,这仅适用于成员变量。 也就是说,我必须写一些类似的东西:

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

这个功能显然是重复的,我不能把它放在基类中。 注意有两种计算方法:如果有7个类的derived1实例和8个类的derived2实例,则有(a)15个对象实例或(b)0个对象实例。我不关心哪一个,因为我不能这样做(使用合理的实用手段,例如想象100个类,其中一半在库中我无法修改)。

当然,理论上可以创建(某些运行类型标识符)=>的映射。 int count,并使用丑陋,缓慢,(运行时类型)的方法(至少在C#,Java中)。

当然,如果我可以修改派生类,我可以使用复制粘贴(糟糕),宏(我知道),mixins(不是这些语言)等等。但这仍然是真的很难看。

这是具体的问题,但它发生在我身上好几次,我希望能够将静态成员“注入”派生类来优雅地解决问题。

非常感谢。

编辑:感谢很好的答案,在C ++中,它可能也使用CRTP(奇怪的重复模板模式),但不能用于C#/ Java(没有多重继承)。当然,必须有权访问派生类并添加这个基类,所以问题仍然存在(如果没有其他方法,这看起来最好)。

编辑2:使用当前语言看起来不可能。每个类的静态部分都不是继承(并且是正确的),但是没有与每个类关联的继承单例,因此这些问题不能如此优雅地解决。 为了说明一些事情,请看下面的代码:普通成员和静态成员是当前的OOP语言功能,“单身”(或任何单词)成员是我的建议/愿望:

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;

如果语言中出现类似的内容,我的问题的解决方案将简单而优雅:

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

7 个答案:

答案 0 :(得分:10)

在C ++中,您可以使用模板基类来完成。基本上它一个mixin,所以它仍然需要每个类通过继承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> {};

由于使用该模板的每个类都有一个不同的基类,因此它具有不同的 count函数,因此是静态变量{{的不同副本1}}。

从使用派生类作为模板参数实例化的模板类继承的技巧称为CRTP。

答案 1 :(得分:2)

在Java中,您可以使用全局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();

}

如果您不希望依赖番石榴,可以使用Map<Class, Integer>

注意:这只跟踪实例创建,而不是垃圾回收,因此计数永远不会减少。如果您愿意受到性能影响,也可以使用PhantomReference跟踪收集:

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;

    }

}

答案 2 :(得分:1)

我会使用模板。顺便说一下,这是用C ++编写的。

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解决方案:

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侵入性较小,允许对要查询的类型进行运行时选择,但模板的开销要小得多,您可以使用它来单独计算每个派生类,而RTTI只能单独计算最多的派生类。

答案 3 :(得分:1)

在Java中,您可以将计数功能实现到hirachy的公共超类中。

此基类包含一个Map - 将类与实例数相关联。 如果创建了base的实例或其子类之一,则调用构造函数。 构造函数增加了concreate类的实例数。

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

答案 4 :(得分:0)

在.Net中,可以使用泛型来实现此目的。由于type erasure,以下技术无法在Java中使用。

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

现在在您的类中,无论是基类还是子类,请按如下方式使用它:

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

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

您不必在每个课程中都包含实例计数属性,只需在InstanceCounter课程中使用。

int someClassCount = InstanceCounter<SomeClass>.Count;

注意:此示例不要求类继承实例计数器类。

如果有人能够在.Net中烧掉一个超类限制,那么以下内容也会有效:

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>
{
}

然后检索计数:

int someClassCount = InstanceCounter<SomeClass>.Count;

int someClassCount = SomeClass.Count;

Note2 :正如评论中所提到的,使用终结器(~SomeClass)很慢,并且只会在GC实际收集实例时减少计数器。为了解决这个问题,我们必须引入确定性的解放方案。例如,例如实施IDisposable

答案 5 :(得分:0)

令我感到震惊的是,如果你真的希望注入/任何类实际上做一些有用的事情,那么必须以某种方式将它与原始类耦合,无论是通过继承还是直接耦合用方法调用。否则你只有两个轮子独立旋转。

我能想到的唯一选择是使用可以为你计算创作的工厂模式 - 但是你必须通过破解来计算减量,比如将对象明确地交给工厂。

答案 6 :(得分:0)

在C#中的方式立即出现在我的脑海中:

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
{

}

以及如何使用它:

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

可能会以不同的方式完成输出。它不是纯粹的OOP,但也不是这个线程中的C ++或Java示例。但它在继承类时不需要一些代码。

并且不要忘记正确处理你的物品!!