尝试计算派生类的实例,type_id不起作用

时间:2010-09-19 17:39:23

标签: c++ rtti typeinfo

我想从我的班级算出派生的所有实例,我试图这样做:

.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());

当然它仍然无效,但现在我明白为什么......我可以让它起作用吗?

5 个答案:

答案 0 :(得分:5)

构造函数中的

typeid(*this)只生成构造函数的类(你有typeid(this)但是这样做是错误的,因为它只会给你一个指针的type_info)。这被认为是施工期间物体的动态类型。

另一个区别是,在构造期间调用的虚函数不会在派生类中结束,而是在构造期间进行调用的类中。

答案 1 :(得分:1)

Johannes解释了为什么这不起作用。

作为一种可能的解决方法,您可以使用初始化列表将指向派生类实例的指针传递给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指针传递给基类构造函数,但这是解决问题的一种方法。

答案 2 :(得分:0)

您需要从子类构造函数中调用AddInstance以获取“this”指针 是儿童类型。但是,这对每个子类都强加了他们必须实现你的这个“界面”。

C ++不支持Java之类的反射,因此无法以通用方式轻松完成。

答案 3 :(得分:0)

您可以通过从派生类传递类名来使其工作,如下所示。

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

其余代码与您提供的代码相同。

答案 4 :(得分:0)

这是否足以满足您的需求?使用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;
}