2010-09-06 44 views
32

¿Es posible obtener el nombre del objeto también?¿Cómo puedo obtener el nombre de clase de un objeto C++?

#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"; 
} 

donde puedo sacar los nombres, algo así como

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

¿El C++ proporciona ningún mecanismo para lograr esto?

+0

Estoy confundido. ¿Estás relacionando C++ 'class' con una" clase de estudiantes "? Si tiene una 'clase' que representa una clase, y la clase tiene un nombre como" Kiendagarten de la Sra. Gutentag ", debe tener un miembro de datos para almacenar eso como' std :: string'. – Potatoswatter

+0

@Potatoswatter: Ahora, estoy confundido. ¿Que estas preguntando? – Lazer

+1

Hay dos significados para la palabra "clase", y el tipo de clase que tiene los estudiantes no es a lo que se refiere la palabra clave 'clase'. – Potatoswatter

Respuesta

51

Puede mostrar el nombre de una variable utilizando el preprocesador. Por ejemplo

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

salidas

3one A 

en mi máquina. El # cambia una ficha en una cadena, después de procesamiento previo de la línea es

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

Por supuesto, si haces algo como

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

obtendrá

3one B 

como el compilador doesn' t mantener un registro de todos los nombres de la variable.

Como sucede en GCC el resultado de typeid(). Nombre() es el nombre de la clase mutilado, para obtener el demangled version uso

#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; 
} 

que me da

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

Otros compiladores utilizar diferentes esquemas de nombres.

+5

¿Por qué obtenemos '3one'? ¿Qué es '3'? – Lazer

+1

@Lazer No tengo idea, ya que la gente dice que el resultado de 'typeid' es específico de la plataforma. Probablemente sea algo relacionado con el esquema de creación de nombres de C++. –

+1

Si intento 'template class one {};' y uso un 'one ' obtengo '3oneIiE' como nombre de clase, por lo que de hecho tiene que ver con el nombre mangling (Lo que probablemente significa clase con un' 3' nombre de personaje 'uno' con' I' una plantilla argumento 'i' (int)' E' fin de argumentos) –

4

Puede probar con "typeid".

Esto no funciona para el nombre de "objeto", pero USTED conoce el nombre del objeto, por lo que solo tendrá que almacenarlo en alguna parte. Al compilador no le importa lo que haya nombrado un objeto.

Vale la pena tener en cuenta, sin embargo, que la salida de typeid es una cosa específica del compilador, por lo que incluso si produce lo que busca en la plataforma actual, puede que no en otra. Esto puede o no ser un problema para ti.

La otra solución es crear algún tipo de envoltorio de plantilla en el que almacene el nombre de clase. Luego debe usar la especialización parcial para que le devuelva el nombre de clase correcto. Esto tiene la ventaja de trabajar en tiempo de compilación pero es significativamente más complejo.

Editar: Al ser más explícita

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

Luego, para cada clase somethign liek lo siguiente:

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

que incluso podría ser macro'd de la siguiente manera:

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

Permitiendo usted para, simplemente, hacer

DefineClassName(MyClass); 

último para obtener el nombre de la clase que haría lo siguiente:

ClassName<MyClass>::name(); 

Edit2: Abundando aún más, a continuación, había necesidad de poner esta macro "DefineClassName" en cada clase de realizar y definir un " nombre de clase "función que llamaría a la función de plantilla estática.

Edit3: Y pensando en ello ... Su obviamente mala fijación a primera hora de la mañana, ya que puede que también acaba de definir un "nombre de clase()" función miembro de la siguiente manera:

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

el cual puede ser macro'd de la siguiente manera:

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

a continuación, puede simplemente dejar

DefineClassName(MyClass); 

en el cl culo como lo defines ...

+0

recomendaría no permitir el nombre predeterminado de la clase, ya que se anula el objetivo. si queremos que se nombre la clase, ejecútela en cada clase. – YeenFei

+0

Sí, es suficiente. Algunas personas pueden preferir trabajar con cualquier clase, incluso con otras clases. – Goz

+0

Supongo que quería decir #className en lugar de ## className? – usta

13

uso typeid(class).name

// código illustratory suponiendo que incluye a todos/espacios de nombres etc

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

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

Es importante recordar que esto da una nombres de ejecución definido.

Por lo que sé, no hay forma de obtener el nombre del objeto en tiempo de ejecución de manera confiable, p. Ej. 'A' en tu código.

EDIT 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)]; 
} 
+0

@chubsdad: ¿Cuál es el resultado que espera? Obtengo '1A', pero el nombre de clase es' one'. – Lazer

+0

@Lazer: como se señala, esto imprime una cadena definida por la implementación. Remita mi edición si quiere una forma más elegante – Chubsdad

+1

El sitio [cppreference.com dice] (http://en.cppreference.com/w/cpp/language/typeid#Notes) "No hay garantía de que el mismo estándar: : todas las evaluaciones de la expresión typeid en el mismo tipo devolverán la instancia type_info ". Esta media '& typeid (A) == & typeid (A)' podría ser 'false'. Con C++ 11 puede usar ['std :: type_index'] (http://en.cppreference.com/w/cpp/types/type_index). – JojOatXGME

7

¿Quieres [nombre de la clase] a ser 'uno' y [objectname] ser 'A'?

Si es así, esto no es posible. Estos nombres son solo abstracciones para el programador, y en realidad no se usan en el código binario que se genera. Podría dar a la clase un nombre de clase de variable estática, que establezca en 'uno' y una variable normal nombre de objeto que asignaría directamente, a través de un método o el constructor. A continuación, puede consultar estos métodos para los nombres de clases y objetos.

+0

@Alexander: sí. – Lazer

+0

@Lazer: ¿Puedo saber por qué? – Chubsdad

+1

@chubsdad: Eche un vistazo al ejemplo del código en la pregunta. – Lazer

2

Sólo tiene que escribir plantilla simple:

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

struct A {} a; 

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

Una mejora de la respuesta @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(); 
} 

que imprimirá:

class A* 
6

Para obtener el nombre de la clase sin destrozando cosas que se pueden utilizar func macro en el constructor:

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

este si compilador específico. Uno le dará 'MyClass :: MyClass' y el otro' MyClass'. Cuando usa el método regular, producirá 'MyClass :: SomeMethod' o' SomeMethod'. –

Cuestiones relacionadas