tags:

views:

327

answers:

10

Hello,

I'd like to be able to count instances of classes that belong in the same class hierarchy.

For example, let's say I have this:

class A;
class B: public A;
class C: public B;

and then I have this code

A* tempA = new A;
B* tempB = new B;
C* tempC = new C;
C* tempC2 = new C;

printf(tempA->GetInstancesCount());
printf(tempB->GetInstancesCount());
printf(tempC->GetInstancesCount());

the result of that should print
1
1
2

The counting, ideally, should be done internally. Each class, and not some kind of manager, should know how many instances it has.

Any ideas?

Thanks!

+4  A: 

Directly off my head:

  • Create an integer static field in each class. Watch out for integer overflow.
  • Initialize it to 0 in an emulated static constructor.
  • Increment it at each (nonstatic) constructor body. Decrement it in the destructor.
  • GetInstancesCount() is a static function that returns the value of your integer static field.

Note: See Mykola's comments. This would print 4 for A, 3 for B and 2 for C i.e. it would count one instance of B as "one A and one B", and one C as "one A, one B and one C". Which is in a way true, but is not what the question asks for. In other words, my answer is wrong :)

Daniel Daranas
The initialization in my second step might be done automatically? I never remember these details...
Daniel Daranas
Binary Worrier
@Binary Worrier, very nice. I suggest that you add this as an answer.
Daniel Daranas
When you create B your A constructor will be called automatically and thus increment count of A.
Mykola Golubyev
@Mykola - a B is an A :) There are 4 A's in the example, not 1... But, ok, you're right, this wouldn't fulfill the (possibly wrong) specification of the OP's question.
Daniel Daranas
In the question it prints 1 for A.
Mykola Golubyev
@Mikola Yes, this is why I said my answer doesn't fulfill the specification. I'll edit it now.
Daniel Daranas
+2  A: 

Use a static member variable for each class.

struct A {
    A() { mInstances++; }
    ~A() { mInstances--; }
    static size_t mInstances;
    static size_t GetInstancesCount() { return mInstances; }
};

size_t A::mInstances;

int main() {
    A* a = new A;
    A* aa = new A;
    cout << A::GetInstancesCount() << endl;
    delete a;
    delete aa;
    cout << A::GetInstancesCount() << endl;
    return 0;
}
dirkgently
+6  A: 

There is a problem with proposed solutions: when you create B you A constructor will be called automatically and thus increment count of A.

class A
{
public:
    A(bool doCount = true)
    {
        if (doCount)
            ++instanceCount_;
    }

    static std::size_t GetInstanceCount()
    {
        return instanceCount_;
    }

    virtual ~A(){}
private:
    static std::size_t instanceCount_;
};

class B: public A
{
public:
    B(bool doCount = true):A(false)
    {
        if (doCount)
            ++instanceCount_;
    }

    static std::size_t GetInstanceCount()
    {
        return instanceCount_;
    }
private:
    static std::size_t instanceCount_;
};

std::size_t A::instanceCount_ = 0;
std::size_t B::instanceCount_ = 0;
Mykola Golubyev
+1, nice technique. (Although I personally think the OP should rethink his/her criteria.)
j_random_hacker
+1 interesting idea!
Ed Woodcock
+2  A: 

A bit of the "Curiously reoccurring template pattern".

template<typename P>
class Counter
{
    public: Counter(bool inc)
        :m_wasInc(inc)
    {
        if (m_wasInc)
        {   getCount()++;}
    }
           ~Counter()
    {
        if (m_wasInc)
        {   getCount()--;}
    }
    static std::size_t GetInstancesCount()
    {
         return getCount();
    }
    private:
       bool m_wasInc;
       /*
        * Alternatively this can be a static member variable.
        * I just used a function because this means I do not need to declare
        * the variable for each type class. The down size is that it becomes
        * more complex if used in the presence of a DLL's.
        *
        * But that is way easy to change. See Alternative Version below.
        */
       static std::size_t& getCount()
       {
           static std::size_t count = 0;
           return count;
       }
};

class A: public Counter<A>
{
    public: A(bool count = true): Counter<A>(count){}
};

class B: public A,Counter<B>
{
    public: B(bool count = true): A(false), Counter<B>(count){}
};

class C: public A, Counter<C>
{
    public: C(bool count = true): A(false), Counter<C>(count){}
};

template<typename P>
class CounterAlt
{
    public: CounterAlt()
    {    ++count;
    }
    static std::size_t GetInstancesCount()
    {
         return count;
    }
    private:
       static std::size_t count;
};

class D: public CounterAlt<D>
{
};
std::size_t CounterAlt<D>::count = 0;
Martin York
B constructor will call A constructor and will add extra increment which shouldn't take place.
Mykola Golubyev
@Mykola Golubyev: No. B does not inherit from A. Also because Counter is a template function there is a different instantiation for each type it is templatized on.
Martin York
in the question example B inherits from A
Mykola Golubyev
But in the given question, B is derived from A.
Naveen
+1. Counter should probably have a decrementing destructor as well.
j_random_hacker
Added Destructor decrement. Add corrected inheritance to match question.
Martin York
+1  A: 

A very crude way would be:

class A
{
public:
    static int m_instanceCount;
    A(bool derivedInstance = false)
    {
     if(! derivedInstance)
     {
      ++m_instanceCount;
     }
    }
    virtual ~A()
    {
     --m_instanceCount;
    }
    virtual int GetInstanceCount()
    {
     return m_instanceCount;
    }
};

int A::m_instanceCount = 0;


class B : public A
{
public:
    static int m_instanceCount;
    B(bool derivedInstance = false): A(true) 
    {
     if(! derivedInstance)
     {
      ++m_instanceCount;
     }
    }
    virtual ~B()
    {
     --m_instanceCount;
    }
    virtual int GetInstanceCount()
    {
     return m_instanceCount;
    }
};

int B::m_instanceCount = 0;


class C : public B
{
public:
    static int m_instanceCount;
    C(): B(true) {++m_instanceCount;}
    virtual ~C()
    {
     --m_instanceCount;
    }
    virtual int GetInstanceCount()
    {
     return m_instanceCount;
    }
};

int C::m_instanceCount = 0;




void main(int argc,char *argv[])
{   
    A* p1 = new A;
    B* p2 = new B;
    C* p3 = new C;
    C* p4 = new C;
    A* p5 = new A;

    delete p5;

    std::cout<<p1->GetInstanceCount()<<"\n";
    std::cout<<p2->GetInstanceCount()<<"\n";
    std::cout<<p3->GetInstanceCount()<<"\n";
}
Naveen
+1. m_instanceCount should probably be decremented at destruction as well.
j_random_hacker
+1  A: 

This is a simple counter I use each so often for debugging:

// counter.hpp
#ifndef COUNTER_HPP
#define COUNTER_HPP

template <typename T>
class Counter
{
public:
    Counter( bool do_count = true ) : counted(do_count) 
    { if ( counted ) get_count()++; }
    ~Counter() { if (counted) --count_; }

    static unsigned long count() { return count_; }
    static unsigned long& get_count() { 
       static unsigned long count=0;
       return count;
    }
private:
    bool do_count;
};
#endif

The usage is simple, just inherit from it:

class BaseClass : public Counter<BaseClass>
{
public:
   explicit BaseClass( bool do_count = true ) 
      : Counter<BaseClass>( do_count )
   {}
};
class DerivedClass : public BaseClass, Counter<DerivedClass>
{
public:
   explicit DerivedClass( bool do_count = true )
      : BaseClass(false), Counter<DerivedClass>(do_count)
   {}
};

User code will call a parameterless constructor:

int main() {
   BaseClass b; // will call Counter<BaseClass>(true)
   DerivedClass d; // will call Counter<BaseClass>(false), Counter<DerivedClass>(true)
}
David Rodríguez - dribeas
+1. But as Martin York says, you will also need to add a definition for Counter<T>::count_ for each T that you use.
j_random_hacker
Not with the current implementation. The counter is an static function variable. It will be created on the first call to get_counter() that happens during construction of the first counted element of the class.
David Rodríguez - dribeas
OK, I guess you mean "Not with the new implementation" -- you must have changed the code around the same time as I commented. Yes, the latest version doesn't require per-type definitions -- much nicer.
j_random_hacker
A: 

Create the classes through a factory so the factory will be able to keep track of how many have been created.

Not as clean as just calling new on the class but it may do what you need.

James Black
A: 

The current solutions all seem to count in constructors, and therefore also count in constructors of base type. (except Mykola, but that solution has an implicit conversion from bool - bad) This double-counting wasn't desired. The tricky part, though, is that during the construction of a derived type, the object temporary has intermediate base types. And with virtual base classes, this situation is even worse. In that case, the intermediate type isn't even well-defined.

Ignoring such transients, there are a few solutions to the double-counting:

  • Use a flag to disable counting
  • Use a protected base class constructor which doesn't count
  • Decrease the instance count of your base classes in derived constructors '*(this->base::count)--`
  • Keep the counts in a dedicated virtual base class.

The latter option (virtual base class) is probably the nicest; the compiler will enforce it's initialized once and only once, from the final ctor.

class counter {
    template<typename T> struct perTypeCounter { static int count =  0; }
    int* count; // Points to perTypeCounter<MostDerived>::count

    protected:
    template<typename MostDerived> counter(MostDerived*) 
    {
        count = &perTypeCounter<MostDerived>::count;
        ++*count;
    }
    ~counter() { --*count; }
};
MSalters
A: 

You can use virtual inheritance to force the counting base class constructor to be called by every super class constructor and only that constructor. This fixes the double counting problem. Then use RTTI for the rest:

#include <map>
#include <typeinfo>
#include <iostream>

struct type_info_less
{
    bool operator() (const std::type_info * t1, const std::type_info * t2) {
     return t1->before(*t2);
    }
};

struct InstCounter
{
    typedef std::map<const std::type_info *, size_t, type_info_less> CountMap;
    static CountMap countMap_;
    const std::type_info * my_type_;

    InstCounter(const std::type_info & type) : my_type_(&type){
        ++countMap_[my_type_];
    }

    ~InstCounter() {
        --countMap_[my_type_];
    }

    static size_t getCount(const std::type_info & type) {
     return countMap_[&type];
    }

    size_t getCount() {
     return countMap_[my_type_];
    }
};

InstCounter::CountMap InstCounter::countMap_;

struct A : public virtual InstCounter
{
    A() : InstCounter(typeid(A)) {}
};

struct B : public A
{
    B() : InstCounter(typeid(B)) {}
};

struct C : public B
{
    C() : InstCounter(typeid(C)) {}
};


int main(int argc, char * argv[])
{
    std::cout << "A: " << InstCounter::getCount(typeid(A)) << std::endl;
    std::cout << "B: " << InstCounter::getCount(typeid(B)) << std::endl;
    std::cout << "C: " << InstCounter::getCount(typeid(B)) << std::endl;

    {
        A a1, a2, a3;
        B b1;
        C c1, c2;

        std::cout << "A: " << InstCounter::getCount(typeid(A)) << std::endl;
        std::cout << "A: " << a1.getCount() << std::endl;
        std::cout << "B: " << InstCounter::getCount(typeid(B)) << std::endl;
        std::cout << "B: " << b1.getCount() << std::endl;
        std::cout << "C: " << InstCounter::getCount(typeid(C)) << std::endl;
        std::cout << "C: " << c1.getCount() << std::endl;
    }

    std::cout << "A: " << InstCounter::getCount(typeid(A)) << std::endl;
    std::cout << "B: " << InstCounter::getCount(typeid(B)) << std::endl;
    std::cout << "C: " << InstCounter::getCount(typeid(C)) << std::endl;

    return 0;
}

This results in

A: 0
B: 0
C: 0
A: 3
A: 3
B: 1
B: 1
C: 2
C: 2
A: 0
B: 0
C: 0

Unfortunately you must store the type for each instance as calling typeid(*this) in InstCounter::getCount() returns the type_info for InstCounter and not the most derived type. (At least with VS2005)

NOTE: Each constructor for A, B and C have to call the InstCounter constructor explicitly then pass their typeid in. There is a down side to this, if you copy and paste this code for each new sub class you can forget to change the parameter to typeid.

Updated to add decrementing destructor.

iain
A: 

One way to get around the double-counting when counting objects in the constructor is to count the objects at the point of need, rather than in the constructor, using RTTI. This is minimally intrusive:

#include <vector>
#include <typeinfo>
#include <iostream>
#include <functional>


class A
{

public:

    A();
    virtual ~A() { }

};

class B: public A
{

public:

    virtual ~B() { }
};

class C: public B
{

public:

    virtual ~C() { }

};

template<class T>
struct TypeIdsEqual: public std::binary_function<T, T, bool>
{
    bool operator() (const T& obj1, const T& obj2) const
    {
        return typeid(*obj1) == typeid(*obj2);
    }
};

struct Counter
{
    static std::vector<A*> objects;

    static void add(A* obj)
    {
        objects.push_back(obj);
    }

    static int count(A* obj)
    {
        return std::count_if(objects.begin(), objects.end(),
                             std::bind1st(TypeIdsEqual<A*>(), obj));
    }

};

std::vector<A*> Counter::objects;

// One intrusive line in the base class constructor.  None in derived classes...
A::A()
{
    Counter::add(this);
}

int main(int *argc, char* argv[])
{
    A* a  = new A;
    B* b  = new B;
    C* c  = new C;
    C* c2 = new C;
    std::cout << Counter::count(*a) << std::endl;  // Output: 1
    std::cout << Counter::count(*b) << std::endl;  // Output: 1
    std::cout << Counter::count(*c) << std::endl;  // Output: 2
}
Andy