2010-09-19 30 views
1

Je veux compter toutes les instances de derivers de ma classe, je suis en train de le faire comme ceci:Essayer de compter des instances de classes dérivées, type_id ne fonctionne pas

.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 héritières qui ressemblent à ceci (le cpp contient des implémentations vides):

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

ce qui est la fonction principale:

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

    Parant::PrintInstances(); 
.... 

Le résultat que je reçois est:

typename: [email protected]@ ;; count: 3 

Pourquoi ça ne marche pas?

je l'ai changé pour

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

Bien sûr, il ne fonctionne toujours pas, mais maintenant je comprends pourquoi ... puis-je faire fonctionner?

+1

juste un petit détail, Parant devrait probablement être parent? – Anycorn

+0

merci :-). d'autres charachters pour le bien de stackoverflow –

+0

Je soupçonne d'une façon ou d'une autre, l'Enfant doit dire au Parent ce que c'est. – UncleBens

Répondre

5

typeid(*this) dans un constructeur donne simplement la classe du constructeur (vous l'aviez typeid(this) mais c'est faux de toute façon car il vous donnera juste le type_info d'un pointeur). C'est considéré comme le type dynamique de l'objet pendant la construction. Une autre différence réside dans le fait que les fonctions virtuelles appelées pendant la construction ne finiront pas dans la classe dérivée, mais dans la classe où l'appel est effectué pendant la construction.

+0

ive ajouté des choses dans l'édition. Puis-je le faire fonctionner? –

+0

@Hellfrost: Il est clair à partir de la réponse que 'typeid' ne fonctionnera pas comme prévu dans le constructeur. Je suppose que le seul moyen est d'appeler séparément une autre fonction * après que * l'objet a été créé. – casablanca

0

Vous devez appeler l'AddInstance à partir des constructeurs de classe enfant pour le pointeur "this" à être de type classe enfant. Mais cela impose à chaque classe d'enfants de mettre en place cette «interface». C++ ne supporte pas la réflexion comme Java, ce qui ne peut pas être fait facilement de manière générique.

+0

boost fait quelque chose de similaire avec les macros, donc c'est possible. de toute façon le pointeur pointe sur l'objet correct avec le bon pointeur v_table donc ceci devrait être possible –

0

Vous pouvez le faire fonctionner en transmettant le nom de classe de la 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.

1

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 Parent en utilisant la liste d'initialisation:

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

Cependant, dans le constructeur Parent, si vous déréférencer ce pointeur, typeid sera encore vous dire 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 vous devez passer explicitement le pointeur this au constructeur de la classe de base, mais c'est une façon de "résoudre" le problème.

+0

ive a copié exactement ce que vous avez offert, pourquoi veut-il cela: ChildA :: ChildA (void): Parant (* this). pour compiler –

+0

@Hellfrost: Si votre classe 'Child' ressemble exactement à cela et que le constructeur' Parent' ressemble exactement à cela, il ne devrait y avoir aucune erreur; êtes-vous sûr que votre constructeur 'Parent' prend un' T * 'et non un' T'? –

+0

Je pense, Passer non construit ce pointeur du constructeur dérivé au constructeur de base est dangereux. Que dis-tu ? – bjskishore123

0

Est-ce que cela répond à 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; 
}