使用typeid获取派生类的名称

Using typeid to get name of derived class

本文关键字:派生 typeid 获取 使用      更新时间:2023-10-16

我正在创建一个资源管理器,它接受从类Resource派生的类。我的问题是typeid(..).name()返回错误的类名。

我想添加像纹理这样的资源到一个地图:

typedef std::unordered_map<std::string, Resource*> ResourceMap;

我想'类+计数器'的名称是资源的字符串/名称。不幸的是,所有东西最终都被称为P8Resource,因为这些类派生自"资源"。这是一个不合时宜的困境,但typeid必须知道texture是一个纹理,而不是资源。

class Resource {}; // abstract
class Texture : public Resource {};
Resource *texture = new Texture();
Texture *texture2 = new Texture();
typeid(texture).name(); // yields 'Resource'
typeid(texture2).name(); // yields 'Texture'

我如何使第一个typeid语句产生"纹理"而不使用类型转换?

typeid的指针将始终是声明的类型,因为这是指针本身的实际类型。要知道指针所指向对象的实际类型,需要对指针解引用以获得实际类型:http://ideone.com/FYmp79

#include <iostream>
#include <typeinfo>
using namespace std;
struct Resource {
    virtual ~Resource() = default;
};
struct Texture : Resource {};
int main() {
    Resource *resource = new Resource;
    Resource *texture = new Texture;
    cout << typeid(*resource).name() << endl; // yields 'Resource'
    cout << typeid(*texture).name() << endl; // yields 'Texture'
    return 0;
}

编辑:正如其他人所说,您需要使类多态以获得运行时类型信息。

我的问题是typeid(..).name()返回错误的名称类。

typeid(...).name()仅用于调试或日志目的。正如http://en.cppreference.com/w/cpp/types/type_info/name所说:

没有给出保证,特别是,返回的字符串可以是对于几种类型相同,并且在相同的调用之间更改程序。

关于你的问题,

我如何使第一个typeid语句产生'Texture'没有使用类型转换?

最安全、最简单和最正确的方法是在Resource中添加自己的虚拟名称函数:

virtual std::string name() const = 0;

然后在每个子类中重写它以返回类名。

首先,typeid只能为多态类类型的值提供动态运行时类型识别。你的类不是多态的。为了"激活"typeid的动态行为,你需要在基类中至少有一个虚方法。

其次,为了使用typeid来确定多态对象的动态类型,您必须将它应用于对象本身,而不是对象的指针(如在您的代码中)。

第三,name()返回的值没有多大意义,不能用于任何实际目的。形式上,name()每次都可以简单地返回一个空字符串。您必须使用(并比较)type_info对象本身来进行运行时类型标识。

typeid操作符只能返回表达式的动态类型,如果该表达式是多态类的对象的glvalue。

如果一个类直接或在它的一个基类中定义了至少一个虚成员函数,则该类是多态的

由于Resource类和派生类不满足此要求,typeid操作符只能访问静态类型

要解决这个问题,以及当您尝试delete指向此类资源的指针时将遇到的问题,请将析构函数设置为virtual。

如果您正在使用Qt库,您可以使用元对象来区分资源实例。

class Resource : public QObject {
    Q_OBJECT
}; // abstract
class Texture : public Resource {
    Q_OBJECT
};
Resource *texture = new Texture();
Resource *resource = new Resource();
texture ->metaObject()->className(); // yields 'Texture'
resource ->metaObject()->className(); // yields 'Resource'