如何从C++对象获取类名?

Laz*_*zer 50 c++ class

是否有可能获得对象名称?

#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";
}
Run Code Online (Sandbox Code Playgroud)

在哪里我可以取名字,像

[classname] = A.classname() = one
[objectname] = A.objectname() = A
Run Code Online (Sandbox Code Playgroud)

C++是否提供了实现此目的的任何机制?

Sco*_*les 72

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

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

输出

3one    A
Run Code Online (Sandbox Code Playgroud)

在我的机器上.该#更改的标记成一个字符串,预处理线后

std::cout<<typeid(A).name()<<"\t"<< "A" <<"\n";
Run Code Online (Sandbox Code Playgroud)

当然,如果你做的事情

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

你会得到

3one B
Run Code Online (Sandbox Code Playgroud)

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

正如在gcc中发生的那样,typeid().name()的结果是受损的类名,以获得demangled版本的使用

#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;
}
Run Code Online (Sandbox Code Playgroud)

这给了我

one<int, one<double, int> > A
Run Code Online (Sandbox Code Playgroud)

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

  • 为什么我们得到`3one`?什么是`3`? (12认同)
  • “3”可能是名字“一”的长度。名称修改将长度添加到名称前面。 (5认同)
  • @Lazer 不知道,正如人们所说,`typeid` 的结果是特定于平台的。这可能与 C++ 的名称修改方案有关。 (3认同)
  • 如果我尝试`template &lt;typename T&gt; class one{};`并使用`one&lt;int&gt;`我得到`3oneIiE`作为类名,所以它确实与名称修改有关(这些东西可能意味着类与一个 `3` 字符名称 `one` 和 `I` 一个模板参数 `i`(int)`E` 参数结尾) (3认同)

Chu*_*dad 18

使用 typeid(class).name

//插图代码假设所有包含/命名空间等

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

struct A{};
int main(){
   cout << typeid(A).name();
}
Run Code Online (Sandbox Code Playgroud)

重要的是要记住,这给出了一个实现定义的名称.

据我所知,没有办法在运行时可靠地获取对象的名称,例如代码中的"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)];
}
Run Code Online (Sandbox Code Playgroud)


gru*_*nge 10

要获取没有修改内容的类名,可以在构造函数中使用func宏:

class MyClass {
    const char* name;
    MyClass() {
        name = __func__;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 此si编译器特定。一个会给你MyClass :: MyClass另一个会给你MyClass。当使用常规方法时,将产生`MyClass :: SomeMethod`或`SomeMethod`。 (2认同)

Ale*_*rty 8

你想[classname]是'one'而[objectname]是'A'吗?

如果是这样,这是不可能的.这些名称只是程序员的抽象,实际上并不用于生成的二进制代码.您可以为类提供一个静态变量classname,您可以将其设置为"one",也可以通过方法或构造函数直接赋值的普通变量objectname.然后,您可以查询这些方法以获取类和对象名称.

  • @Lazer:是的,对于那个操作,使用`if(typeid(a)== typeid(int))`; 不需要乱用字符串.然而,首先不需要该操作,因为静态类型是已知的.如果编译器知道静态类型但是灵活,就像在模板中一样,你应该使用`<type_traits>`模板操作,例如`is_same <type_of_a,int>`而不是运行时比较.`typeid`仅用于具有虚方法的类或用于调试模板的实际目的. (2认同)

sea*_*-kg 7

只需编写简单的模板:

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

struct A {} a;

void main() {
   std::cout << getClassName(a);
}
Run Code Online (Sandbox Code Playgroud)


Goz*_*Goz 6

您可以尝试使用"typeid"

这不适用于“对象”名称,但您知道对象名称,因此您只需要将其存储在某处。Compiler 不关心您命名对象的名称。

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

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

编辑:更明确

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

然后对于每个类的东西如下:

template<> class ClassName<MyClass>
{
public:
    static std::string name()
    {
        return "MyClass";
    }
};
Run Code Online (Sandbox Code Playgroud)

甚至可以是宏如下:

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

让你,简单地,做

DefineClassName( MyClass );
Run Code Online (Sandbox Code Playgroud)

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

ClassName< MyClass >::name();
Run Code Online (Sandbox Code Playgroud)

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

Edit3:考虑一下......早上第一件事显然很糟糕,因为你不妨定义一个成员函数“classname()”,如下所示:

std::string classname()
{
     return "MyClass";
}
Run Code Online (Sandbox Code Playgroud)

可以按如下方式进行宏处理:

DefineClassName( className ) \
std::string classname()  \
{ \
     return #className; \
}
Run Code Online (Sandbox Code Playgroud)

然后你可以简单地放下

DefineClassName( MyClass );
Run Code Online (Sandbox Code Playgroud)

在你定义它的时候进入类......