Counting instances of individual derived classes

前端 未结 10 844
南笙
南笙 2021-01-06 04:42

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: pu         


        
相关标签:
10条回答
  • 2021-01-06 05:05

    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;
    
    0 讨论(0)
  • 2021-01-06 05:05

    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;
    
    0 讨论(0)
  • 2021-01-06 05:07

    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
    }
    
    0 讨论(0)
  • 2021-01-06 05:09

    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.

    0 讨论(0)
提交回复
热议问题