【问题标题】:How can I get the class name from a C++ object?如何从 C++ 对象中获取类名?
【发布时间】:2011-04-08 15:15:26
【问题描述】:

是否也可以获取对象名称?

#include<cstdio>

class one {
public:
    int no_of_students;
    one() { no_of_students = 0; }
    void new_admission() { no_of_students++; }
};

int main() {
    one A;
    for(int i = 0; i < 99; i++) {
        A.new_admission();
    }
    cout<<"class"<<[classname]<<" "<<[objectname]<<"has "
        <<A.no_of_students<<" students";
}

我可以在哪里获取名称,例如

[classname] = A.classname() = one
[objectname] = A.objectname() = A

C++ 是否提供任何机制来实现这一点?

【问题讨论】:

  • 我很困惑。您是否将 C++ class 与“学生班”联系起来?如果您有一个代表一个班级的class,并且该班级的名称类似于“古腾塔格夫人的幼儿园”,它应该有一个数据成员将其存储为std::string
  • @Potatoswatter:现在,我很困惑。你在问什么?
  • “班级”一词有两种含义,而有学生的班级不是class关键字所指的那种。
  • @Potatoswatter:哦,我明白了。不,我只想在此处检索 class 名称,即 one

标签: c++ class


【解决方案1】:

您可以尝试使用"typeid"

这不适用于“对象”名称,但您知道对象名称,因此您只需将其存储在某个地方。 Compiler 并不关心你为对象命名的内容。

不过,值得记住的是,typeid 的输出是编译器特定的东西,所以即使它在当前平台上产生了你想要的东西,它也可能不会在另一个平台上产生。这对您来说可能是也可能不是问题。

另一种解决方案是创建某种模板包装器来存储类名。然后你需要使用部分特化来让它为你返回正确的类名。这具有工作编译时间的优势,但要复杂得多。

编辑:更明确

template< typename Type > class ClassName
{
public:
    static std::string name()
    {
        return "Unknown";
    }
};

那么对于每个类,下面是一些事情:

template<> class ClassName<MyClass>
{
public:
    static std::string name()
    {
        return "MyClass";
    }
};

甚至可以如下宏化:

#define DefineClassName( className ) \
\
template<> class ClassName<className> \
{ \
public: \
    static std::string name() \
    { \
        return #className; \
    } \
}; \

让你简单地去做

DefineClassName( MyClass );

最后要获取类名,您需要执行以下操作:

ClassName< MyClass >::name();

Edit2:进一步阐述,您需要将这个“DefineClassName”宏放入您创建的每个类中,并定义一个“类名”函数来调用静态模板函数。

Edit3:想一想……早上第一件事发帖显然很糟糕,因为您不妨定义一个成员函数“classname()”,如下所示:

std::string classname()
{
     return "MyClass";
}

可以宏化如下:

DefineClassName( className ) \
std::string classname()  \
{ \
     return #className; \
}

然后你可以简单地放下

DefineClassName( MyClass );

进入你定义的类...

【讨论】:

  • 我建议不要允许默认类名,因为它会破坏目的。如果我们希望为类命名,则在每个类上强制执行它。
  • 是的,很公平。有些人可能更喜欢它与任何课程甚至其他课程一起使用。
  • 我猜你的意思是#className而不是##className?
  • 是的……对不起……我当时没有可以测试编译它的机器。
【解决方案2】:

使用typeid(class).name

// 假设所有包含/命名空间等的说明性代码

#include <iostream>
#include <typeinfo>
using namespace std;

struct A{};
int main(){
   cout << typeid(A).name();
}

记住这一点很重要 给出实现定义的名称。

据我所知,没有办法在运行时可靠地获取对象的名称,例如'A' 在你的代码中。

编辑 2:

#include <typeinfo>
#include <iostream>
#include <map>
using namespace std; 

struct A{
};
struct B{
};

map<const type_info*, string> m;

int main(){
    m[&typeid(A)] = "A";         // Registration here
    m[&typeid(B)] = "B";         // Registration here

    A a;
    cout << m[&typeid(a)];
}

【讨论】:

  • @chubsdad:您期望的输出是什么?我得到1A,但类名是one
  • @Lazer:正如所指出的,这会打印一个实现定义的字符串。如果您想要更花哨的方式,请参考我的编辑
  • 网站cppreference.com says "不能保证相同类型的 typeid 表达式的所有计算都会返回相同的 std::type_info 实例。"这意味着&amp;typeid(A) == &amp;typeid(A) 可能是false。使用 C++11,您可以使用 std::type_index
  • 可能会使用更好的哈希值,例如typeid(A).hash_code()
【解决方案3】:

您希望 [classname] 为 'one' 而 [objectname] 为 'A' 吗?

如果是这样,这是不可能的。这些名称只是程序员的抽象,实际上并没有在生成的二进制代码中使用。你可以给类一个静态变量类名,你设置为“一”和一个普通的变量对象名,你可以通过方法或构造函数直接分配。然后,您可以在这些方法中查询类和对象名称。

【讨论】:

  • @Lazer:该示例绝对没有理由要检索名称one。这就是我在问题讨论中要问的问题。想要 C++ 类的名称与丑陋的动态类型黑客和调试有关。想要程序所代表的学校班级的名称不是运行时类型信息的工作。
  • @Potatoswatter:正如你所建议的,它与课程无关。例如,如果我有一个变量int a;,C++ 中是否有一个构造允许我执行类似于if(a.type() == int) {} 的操作。这就是我想知道的。
  • @Lazer:是的,对于那个操作,使用if ( typeid(a) == typeid(int) );无需乱用字符串。但是,首先不需要该操作,因为静态类型是已知的。如果编译器知道静态类型但很灵活,如在模板中,则应使用&lt;type_traits&gt; 模板操作,例如is_same&lt; type_of_a, int &gt;,而不是运行时比较。 typeid 仅用于具有虚拟方法的类或调试模板的实际用途。
【解决方案4】:

您可以使用预处理器显示变量的名称。比如

#include <iostream>
#define quote(x) #x
class one {};
int main(){
    one A;
    std::cout<<typeid(A).name()<<"\t"<< quote(A) <<"\n";
    return 0;
}

输出

3one    A

在我的机器上。 # 将标记更改为字符串,预处理后的行是

std::cout<<typeid(A).name()<<"\t"<< "A" <<"\n";

当然,如果你做类似的事情

void foo(one B){
    std::cout<<typeid(B).name()<<"\t"<< quote(B) <<"\n";
}
int main(){
    one A;
    foo(A);
    return 0;
}

你会得到

3one B

因为编译器不会跟踪所有变量的名称。

正如在 gcc 中发生的那样, typeid().name() 的结果是损坏的类名,以获取 demangled version 使用

#include <iostream>
#include <cxxabi.h>
#define quote(x) #x
template <typename foo,typename bar> class one{ };
int main(){
    one<int,one<double, int> > A;
    int status;
    char * demangled = abi::__cxa_demangle(typeid(A).name(),0,0,&status);
    std::cout<<demangled<<"\t"<< quote(A) <<"\n";
    free(demangled);
    return 0;
}

这给了我

one<int, one<double, int> > A

其他编译器可能使用不同的命名方案。

【讨论】:

  • 为什么我们得到3one3 是什么?
  • @Lazer 不知道,因为人们说typeid 的结果是特定于平台的。这可能与 C++ 的名称修饰方案有关。
  • 如果我尝试template &lt;typename T&gt; class one{}; 并使用one&lt;int&gt; 我得到3oneIiE 作为类名,所以它确实与名称修改有关(这些东西可能意味着类与3角色名称 oneI 一个模板参数 i (int) E 参数结束)
  • +1 我不知道我们甚至在 C++ 中有这样的功能
  • "3" 可能是名称“one”的长度。名称修饰会在名称前面加上长度。
【解决方案5】:

只写简单的模板:

template<typename T>
const char* getClassName(T) {
  return typeid(T).name();
}

struct A {} a;

void main() {
   std::cout << getClassName(a);
}

【讨论】:

    【解决方案6】:

    @Chubsdad 答案的改进,

    //main.cpp
    
    using namespace std;
    
    int main(){
    A a;
    a.run();
    }
    
    //A.h
    class A{
    public:
     A(){};
     void run();
    }
    
    //A.cpp
    #include <iostream>
    #include <typeinfo>
    void A::run(){
       cout << (string)typeid(this).name();
    }
    

    将打印的内容:

    class A*
    

    【讨论】:

    • 不,我的 g++ 6.3 编译器不会打印 A*,而是“P1A”,因为 typeid 名称取决于实现。
    【解决方案7】:

    要获得类名而不修改内容,您可以在构造函数中使用 func 宏:

    class MyClass {
        const char* name;
        MyClass() {
            name = __func__;
        }
    }
    

    【讨论】:

    • 这个 si 编译器特定的。一个会给你MyClass::MyClass 另一个MyClass。当以常规方法使用时,它将产生MyClass::SomeMethodSomeMethod
    【解决方案8】:

    你可以试试这个:

    template<typename T>
    inline const char* getTypeName() {
      return typeid(T).name();
    }
    
    #define DEFINE_TYPE_NAME(type, type_name)  \
      template<>                               \
      inline const char* getTypeName<type>() { \
        return type_name;                      \
      }
    
    DEFINE_TYPE_NAME(int, "int")
    DEFINE_TYPE_NAME(float, "float")
    DEFINE_TYPE_NAME(double, "double")
    DEFINE_TYPE_NAME(std::string, "string")
    DEFINE_TYPE_NAME(bool, "bool")
    DEFINE_TYPE_NAME(uint32_t, "uint")
    DEFINE_TYPE_NAME(uint64_t, "uint")
    // add your custom types' definitions
    

    然后这样称呼它:

    void main() {
     std::cout << getTypeName<int>();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-09
      • 1970-01-01
      • 1970-01-01
      • 2014-01-22
      • 1970-01-01
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多