Essayer de compter des instances de classes dérivées, type_id ne fonctionne pas

StackOverflow https://stackoverflow.com/questions/3746742

  •  04-10-2019
  •  | 
  •  

Question

Je veux compter toutes les instances de derivers de ma classe, je suis en train de le faire comme ceci:

.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

fichier .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 ;
    }

}

J'ai deux qui héritières ressemblent à ceci (le cpp contient des implémentations vides):

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

et c'est la fonction principale:

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

    Parant::PrintInstances();
....

Le résultat que je reçois est:

 typename: .PAVParant@@ ;; count: 3

Pourquoi ça ne marche pas?

Je l'ai changé à

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

Bien sûr, il ne fonctionne toujours pas, mais maintenant je comprends pourquoi ... puis-je obtenir au travail?

Était-ce utile?

La solution

typeid(*this) dans un constructeur donne juste la classe du constructeur (vous l'aviez typeid(this) mais c'est faux de toute façon, car il va simplement vous donner le type_info d'un pointeur). Ce qui est considéré comme le type de dynamique de l'objet lors de la construction.

Une autre différence il y a que des fonctions virtuelles appelées lors de la construction ne se retrouveront pas dans la classe dérivée, mais dans la classe où l'appel est effectué pendant la construction.

Autres conseils

Johannes explique pourquoi cela ne fonctionne pas.

Pour contourner ce problème possible, vous pouvez passer un pointeur sur l'instance de classe dérivée au constructeur de Parent en utilisant la liste d'initialisation:

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

Cependant, dans le constructeur de Parent, si vous déréférencer ce pointeur, typeid vous dira encore que son type dynamique est Parent. Vous pouvez cependant faire le constructeur Parent un modèle:

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

    // ...
};

Ce modèle constructeur sera instancié pour chaque type de classe dérivée, et T sera le type correct de la classe dérivée.

Cette approche devient plus difficile avec plusieurs niveaux d'héritage et il faut que vous passez le pointeur de this au constructeur de classe de base explicitement, mais il est un moyen de « résoudre » le problème.

Vous devez appeler le AddInstance des constructeurs de classe des enfants pour le « ce » pointeur être de type classe enfant. Mais cela impose à chaque classe d'enfants qu'ils ont à mettre en œuvre cette « interface » de la vôtre.

C ++ ne supporte pas la réflexion comme Java si cela ne peut se faire facilement de façon générique.

Vous pouvez le faire fonctionner en passant le nom de classe de classe dérivée comme ci-dessous.

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")
    {
    }
};

Reste du code reste identique à ce que vous avez fourni.

Cela suffit-il à vos besoins? Utilise 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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top