开发者

Trying to count instances of deriving classes, type_id doesn't work

开发者 https://www.devze.com 2023-01-16 14:51 出处:网络
I want to count all the instances of derivers from my class, I\'m trying to do it like so: .h file: #ifndef _Parant

I want to count all the instances of derivers from my class, I'm trying to do it like so:

.h file:

#ifndef _Parant
#define _Parant

#include<map>

class Parant
{
public:
    Parant();
  开发者_JS百科  virtual ~Parant();
    static void PrintInstances();

private:
    static void AddInstance(const char* typeName);
    static std::map<const char*, int> InstanceCounter;
};

#endif

.cpp file:

#include "Parant.h"
#include <typeinfo>
#include <iostream>

using namespace std;

Parant::Parant()
{
    AddInstance(typeid(this).raw_name());
}


Parant::~Parant()
{
}


std::map<const char*, int> Parant::InstanceCounter;

void Parant::AddInstance(const char* typeName)
{
    InstanceCounter[typeName]++;
}


void Parant::PrintInstances()
{
    for(map<const char*,int>::iterator i = InstanceCounter.begin(); i != InstanceCounter.end(); i++)
    {
        cout << " typename: " << i -> first << " ;;" ;
        cout << " count: " << i -> second << endl ;
    }

}

I have two inheritors that look like this (the cpp contains empty implementations):

#pragma once
#include "parant.h"
class ChildA :
    public Parant
{
public:
    ChildA(void);
    virtual ~ChildA(void);
};

and this is the main function:

int main()
{
    ChildA a;
    ChildB b;
    ChildA a1;

    Parant::PrintInstances();
....

The result I get is:

 typename: .PAVParant@@ ;; count: 3

Why doesn't it work?

I changed it to

AddInstance(typeid(*this).raw_name());

of course it still doesn't work, though now I understand why... can I get it to work?


typeid(*this) in a constructor just yields the constructor's class (you had it typeid(this) but that's wrong anyway since it will just give you the type_info of a pointer). That's considered the dynamic type of the object during construction.

Another difference there is that virtual functions called during construction won't end up in the derived class, but in the class where the call is made during construction.


Johannes explains why this doesn't work.

As a possible workaround, you can pass a pointer to the derived class instance to the Parent constructor using the initialization list:

struct ChildA : Parent 
{
    ChildA() : Parent(this) { }
};

However, in the Parent constructor, if you dereference this pointer, typeid will still tell you that its dynamic type is Parent. You can, however, make the Parent constructor a template:

struct Parent
{
    template <typename T>
    Parent(T* x)
    {
       AddInstance(typeid(T).raw_name());
    }

    // ...
};

This constructor template will be instantiated for each derived class type, and T will be the correct type of the derived class.

This approach becomes more difficult with multiple levels of inheritance and it requires that you pass the this pointer to the base class constructor explicitly, but it's one way to "solve" the problem.


You need to call the AddInstance from the child class constructors for the "this" pointer to be of child class type. But that imposes on each child class that they have to implement this "interface" of yours.

C++ doesn't support reflection like Java so this can't be done easily in a generic fashion.


You can make it work by passing class name from derived class like below.

class Parent
{
public:
   Parent(const char* pClassName) //Gets called for every derived object with
   {                              //corresponding derived class name as parameter.
       AddInstance(pClassName);  //Instance count will be incremented here.
   }
};

class ChildA: public Parent
{
public:
    ChildA()
      : Parent("ChildA") //Pass the child class name to Parent.
    {
    }
};
class ChildB: public Parent
{
public:
    ChildB()
      : Parent("ChildB")
    {
    }
};

Rest of the code remains same as what you provided.


Does this suffice your need? Uses CRTP

map<string, int> m;

template<class T> struct Base{
    Base(){
        m[typeid(T).name()]++;    // potentially unsafe
    }
};

struct Derived : Base<Derived>{
};

struct AnotherDerived : Base<AnotherDerived>{
};

int main(){
    Derived d1, d2;
    AnotherDerived d11, d21;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消