количество экземпляров класса для каждого производного класса - PullRequest
14 голосов
/ 10 января 2011

есть ли способ заставить все производные классы считать их экземпляры? Как (написать код на одном из 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; }
}

Эта функциональность явно повторяется, и я не могу поместить ее в базовый класс. Обратите внимание, что существует 2 способа вычисления: если имеется 7 экземпляров класса производного 1 и 8 экземпляров класса производного 2, то существует (а) 15 экземпляров объекта или (б) 0 экземпляров объекта. Мне все равно, какой из них, потому что я не могу сделать ни то, ни другое (используя достаточно практичные средства, например, представьте 100 классов, половина из которых находится в библиотеке, которую я не могу изменить).

Конечно, теоретически можно создать карту (некоторый идентификатор типа типа выполнения) => int count и использовать уродливый, медленный (основанный на времени выполнения) подход (по крайней мере, в C #, Java). ).

Конечно, если я могу модифицировать производные классы, я могу использовать копирование-вставку (ужасно), макрос (да, я знаю), миксины (не на этих языках) и т. Д. Но это по-прежнему ужасно.

Это специфическая проблема, но со мной несколько раз случалось, что я хотел бы иметь возможность "внедрить" статический член в производный класс для элегантного решения проблемы.

Помощь очень ценится.

РЕДАКТИРОВАТЬ: спасибо за хороший ответ, в C ++ возможно также использовать CRTP (Любопытно повторяющийся шаблон), но не в C # / Java (без множественного наследования). Конечно, нужно иметь доступ к производным классам и добавить этот базовый класс, поэтому вопрос остается (если нет другого пути, это выглядит лучше).

РЕДАКТИРОВАТЬ 2: выглядит невозможно с текущими языками. Статическая часть каждого класса не наследуется (и это правильно), но нет наследующего синглтона, связанного с каждым классом, поэтому проблемы такого рода не могут быть решены так элегантно. Чтобы проиллюстрировать это, взгляните на следующий код: обычные и статические члены являются текущей функцией языков ООП, члены-одиночки (или каковы бы ни были слова) являются моим предложением / желанием:

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 ]

10 голосов
/ 10 января 2011

В C ++ вы можете сделать это с помощью базового класса шаблона.По сути, это является миксином, поэтому он все еще требует взаимодействия каждого класса путем наследования от миксина:

// 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 функцию и, следовательно, другую копию статической переменной counter.

Хитрость наследования от класса шаблона, который создается с использованиемпроизводный класс в качестве параметра шаблона называется CRTP.

2 голосов
/ 10 января 2011

В 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 s, если хотите получить удар по производительности:

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;

    }

}
1 голос
/ 10 января 2011

В Java вы можете реализовать функцию подсчета для общего суперкласса вашей библиотеки.

Этот базовый класс содержит Map - связь классов с количеством экземпляров.Если создан экземпляр base или один из его подклассов, вызывается конструктор ins.Конструктор увеличивает количество экземпляров класса 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());
        }
    }
}
1 голос
/ 10 января 2011

Я бы использовал шаблон. Кстати, это на 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 может подсчитывать только самые производные классы по отдельности.

0 голосов
/ 10 января 2011

Путь в 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>());

Вывод может быть сделан иначе, возможно.И это не чистый ООП, но и примеры C ++ или Java в этой теме.Но это не требует немного кода для наследования класса.

И не забывайте о правильной утилизации ваших объектов !!

0 голосов
/ 10 января 2011

Мне кажется, что если вы действительно хотите, чтобы внедренный / какой-либо класс действительно делал что-то полезное, то вы должны каким-то образом связать это с исходными классами, будь то наследование или прямая связь с вызовы методов. В противном случае у вас только два колеса вращаются независимо друг от друга.

Единственная альтернатива, которую я могу придумать, - это использовать фабричный шаблон, который может подсчитать для вас творения, - но вам придется взломать что-то, чтобы сосчитать декременты, например, явно вернуть объект на фабрику.

0 голосов
/ 10 января 2011

В .Net для этого могут использоваться дженерики.Следующая техника не будет работать в 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;

Примечание2 : Как упоминалось в комментариях, использование финализатора (~SomeClass) является медленным и будет только уменьшатьсчетчик, когда экземпляр фактически собран GC.Чтобы обойти это, нужно ввести детерминированное «освобождение» экземпляров, например, реализовать IDisposable.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...