Пытаясь считать экземпляры получения классов, type_id не работает
Вопрос
Я хочу считать все случаи получения из моего класса, я пытаюсь сделать это так:
.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
.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 ;
}
}
У меня есть два наследования, которые выглядят так (CPP содержит пустые реализации):
#pragma once
#include "parant.h"
class ChildA :
public Parant
{
public:
ChildA(void);
virtual ~ChildA(void);
};
И это главная функция:
int main()
{
ChildA a;
ChildB b;
ChildA a1;
Parant::PrintInstances();
....
В результате я получаю:
typename: .PAVParant@@ ;; count: 3
Почему это не работает?
Я изменил это на
AddInstance(typeid(*this).raw_name());
Конечно, он все еще не работает, хотя теперь я понимаю, почему ... Могу ли я получить это на работу?
Решение
typeid(*this)
в конструкторе просто дает класс конструктора (у вас было это typeid(this)
Но все равно неправильно, так как он просто даст вам тип_инфо указателя). Это считается динамическим типом объекта во время строительства.
Другое различие Существуют, что виртуальные функции, называемые во время строительства, не будут заканчиваться в полученном классе, но в классе, где вызов сделан во время строительства.
Другие советы
Йоханнес объясняет, почему это не работает.
В качестве возможного обходного пути вы можете пройти указатель на полученный экземпляр класса к Parent
Конструктор с использованием списка инициализации:
struct ChildA : Parent
{
ChildA() : Parent(this) { }
};
Однако в Parent
Конструктор, если вы разыгрываете этот указатель, typeid
все равно скажут вам, что его динамический тип Parent
. Отказ Вы можете, однако, сделать Parent
Конструктор шаблона:
struct Parent
{
template <typename T>
Parent(T* x)
{
AddInstance(typeid(T).raw_name());
}
// ...
};
Этот шаблон конструктора будет создан для каждого производного типа класса, а также T
будет правильный тип полученного класса.
Этот подход становится сложнее с несколькими уровнями наследования, и он требует, чтобы вы проходили this
Указатель на конструктор базового класса явно, но это один из способов «решить» проблему.
Вам необходимо позвонить в AddInstance от конструкторов дочерних классов для указателя «Это», чтобы иметь тип класса ребенка. Но это навязывает каждому ребенку класс, что они должны реализовать этот «интерфейс» вашего.
C ++ не поддерживает отражение как Java, поэтому это не может быть легко сделано в общем моде.
Вы можете заставить его работать, передавая имя класса от полученного класса, как ниже.
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")
{
}
};
Остальная часть кода остается такой же, как то, что вы предоставили.
Это хватает ваша потребность? Использует 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;
}