计算每个派生类的类实例数

2022-09-04 03:16:09

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

想象一下,我已经可以访问根类(例如object),并且所有其他类(直接或间接地)都是从这个类派生的。我想要的是:

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

这个功能显然是重复的,我不能把它放在基类中。请注意,有 2 种计算方法:如果有 7 个类派生 1 实例和 8 个类派生 2 实例,则有 (a) 15 个 object 实例或 (b) 个 object 实例。我不在乎哪一个,因为我不能两者都做(使用相当实用的手段,例如想象100个类,其中一半在我无法修改的库中)。

当然,从理论上讲,可以创建 map(某些运行类型类型标识符)=> int 计数,并使用基于丑陋、缓慢(运行时类型)的方法(至少在 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; }
};

答案 1

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

由于使用模板的每个类都有不同的基类,因此它具有不同的函数,因此静态变量的不同副本。countcounter

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


答案 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>

注意:这只会跟踪实例创建,不会跟踪垃圾回收,因此计数永远不会减少。如果您愿意对性能造成影响,也可以使用 s 跟踪集合: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;

    }

}