派生クラスのインスタンスをカウントしようとすると、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 ;
}
}
このように見える2人の相続人がいます(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)
しかし、とにかく間違っています。なぜなら、それはあなたにポインターのType_Infoを与えるだけだからです)。これは、建設中のオブジェクトの動的なタイプと見なされます。
別の違いは、建設中に呼び出される仮想関数が派生クラスではなく、建設中に呼び出しが行われるクラスで終わることはないことです。
他のヒント
ヨハネスは、なぜこれがうまくいかないのかを説明しています。
可能な回避策として、派生クラスインスタンスへのポインターを渡すことができます。 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
基本クラスのコンストラクターへのポインターは明示的にありますが、問題を「解決」する1つの方法です。
子どものクラスのポインターのために、子供クラスのコンストラクターから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;
}