Tratar de contar las instancias de clases derivadas, TYPE_ID no funciona
Pregunta
Quiero contar todas las instancias de derivers de mi clase, yo estoy tratando de hacerlo de esta manera:
archivo .h:
#ifndef _Parant
#define _Parant
#include<map>
class Parant
{
public:
Parant();
virtual ~Parant();
static void PrintInstances();
private:
static void AddInstance(const char* typeName);
static std::map<const char*, int> InstanceCounter;
};
#endif
archivo .cpp:
#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 ;
}
}
Tengo dos herederos que se ven así (CPP contiene implementaciones vacías):
#pragma once
#include "parant.h"
class ChildA :
public Parant
{
public:
ChildA(void);
virtual ~ChildA(void);
};
y esta es la principal función:
int main()
{
ChildA a;
ChildB b;
ChildA a1;
Parant::PrintInstances();
....
El resultado que obtengo es:
typename: .PAVParant@@ ;; count: 3
¿Por qué no funciona?
He cambiado a
AddInstance(typeid(*this).raw_name());
Por supuesto que sigue sin funcionar, aunque ahora entiendo por qué ... puedo conseguir que funcione?
Solución
typeid(*this)
en un constructor simplemente da clase de constructores (que lo tenía typeid(this)
pero eso es de todos modos equivocada ya que sólo le dará la type_info de un puntero). Eso se considera el tipo dinámico del objeto durante la construcción.
Otra diferencia no es que las funciones virtuales llamadas durante la construcción no va a terminar en la clase derivada, pero en la clase en la que se realiza la llamada durante la construcción.
Otros consejos
Johannes explica por qué esto no funciona.
Como posible solución, puede pasar un puntero a la instancia de la clase derivada al constructor Parent
utilizando la lista de inicialización:
struct ChildA : Parent
{
ChildA() : Parent(this) { }
};
Sin embargo, en el constructor Parent
, si eliminar la referencia de este puntero, typeid
todavía le dirá que su tipo dinámico es Parent
. Puede, sin embargo, que el constructor Parent
una plantilla:
struct Parent
{
template <typename T>
Parent(T* x)
{
AddInstance(typeid(T).raw_name());
}
// ...
};
Esta plantilla constructor se crea una instancia para cada tipo de clase derivada, y T
será el tipo correcto de la clase derivada.
Este enfoque se vuelve más difícil con múltiples niveles de herencia y se requiere que se pase el puntero this
al constructor de la clase base explícita, pero es una manera de "resolver" el problema.
Es necesario llamar a la AddInstance de los constructores de clase hijo para el puntero "this" a ser de tipo clase hija. Pero eso impone a cada clase hija que tienen que poner en práctica esta "interfaz" de los suyos.
C ++ no soporta la reflexión como Java así que esto no se puede hacer fácilmente de forma genérica.
Se puede hacer que funcione pasando nombre de clase de la clase derivada, como a continuación.
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")
{
}
};
Resto del código permanece igual a lo que ya ha proporcionado.
¿Tiene esto suficiente para sus necesidades? Utiliza 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;
}