有没有办法让所有派生类都计算它们的实例?如何(用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; }
};
答案 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示例。但它在继承类时不需要一些代码。
并且不要忘记正确处理你的物品!!