2010-09-06 15 views
32

Czy można uzyskać nazwę obiektu?Jak mogę uzyskać nazwę klasy z obiektu 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"; 
} 

gdzie mogę pobrać nazwy, coś

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

Czy w C++ zapewniają żadnego mechanizmu do osiągnięcia tego celu?

+0

Jestem zdezorientowany. Czy łączysz "klasę C++" z "klasą uczniów"? Jeśli masz "klasę" reprezentującą klasę, a klasa ma nazwę taką jak "Przedszkole Mr Gutentag", powinna ona mieć element danych do przechowywania tego jako 'std :: string'. – Potatoswatter

+0

@Potatoswatter: Teraz jestem zdezorientowany. O co pytasz? – Lazer

+1

Istnieją dwa znaczenia słowa "klasa", a rodzaj zajęć z uczniami nie jest tym, do czego odnosi się słowo "klasa". – Potatoswatter

Odpowiedz

51

Możesz wyświetlić nazwę zmiennej za pomocą preprocesora. Na przykład

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

wyjścia

3one A 

na moim komputerze. # zmienia znak na ciąg znaków, po przerób linii jest

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

Oczywiście jeśli robisz coś

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

dostaniesz

3one B 

jak kompilator nie robi” t śledzić wszystkie nazwy zmiennych.

Jak to się dzieje w gcc wynik typeid(). Nazwa() jest zniekształcone nazwy klasy, aby uzyskać demangled version Użyj

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

który daje mi

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

Inne kompilatory mogą używać różnych schematów nazewnictwa.

+5

Dlaczego otrzymujemy '3one'? Co to jest "3"? – Lazer

+1

@Lazer Nie mam pojęcia, ponieważ ludzie mówią, że wynik 'typeid' jest specyficzny dla platformy. Prawdopodobnie ma to związek ze schematem wymazywania nazw w C++. –

+1

Jeśli wypróbuję 'template class one {};' i użyję 'one ' Otrzymuję '3oneIiE' jako nazwę klasy, więc tak naprawdę ma to związek z manglingiem nazwy (The stuff prawdopodobnie oznacza klasę z' 3' nazwa znaku 'jeden' ​​z' I' jeden argument szablonu 'i' (int)' E' koniec argumentów) –

4

Możesz spróbować użyć opcji "typeid".

To nie działa dla nazwy obiektu, ale TY znasz nazwę obiektu, więc będziesz musiał go gdzieś przechowywać. Kompilator nie dba o to, co nazwałeś obiekt.

Warto jednak pamiętać, że wyjście typu id jest kompilatorem, więc nawet jeśli produkuje to, co jest na obecnej platformie, może nie na innym. To może być lub nie być problemem dla Ciebie.

Drugim rozwiązaniem jest utworzenie jakiegoś szablonu opakowania, w którym przechowujesz nazwę klasy. Następnie musisz użyć częściowej specjalizacji, aby uzyskać poprawną nazwę klasy. Ma to tę zaletę, że działa podczas kompilacji, ale jest znacznie bardziej złożona.

Edit: Być bardziej wyraźne

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

Następnie dla każdej klasy liek somethign następujące:

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

Który może być nawet macro'd następująco:

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

Umożliwienie możesz po prostu wykonać:

DefineClassName(MyClass); 

Wreszcie, aby uzyskać nazwę klasy, którą należy wykonać następujące czynności:

ClassName<MyClass>::name(); 

Edit2: Opracowanie dalej którą następnie trzeba umieścić ten „DefineClassName” makro w każdej klasie można zrobić i zdefiniować " classname "funkcja, która wywołałaby funkcję statycznego szablonu.

Edit3: I o tym myśleć ... jej oczywiście złe księgowania pierwszą rzeczą, w godzinach porannych, jak może równie dobrze zdefiniować funkcję Member „classname()” w następujący sposób:

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

które mogą być macro'd następująco:

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

Następnie można po prostu tylko kropla

DefineClassName(MyClass); 

do cl tyłek podczas definiowania ...

+0

Zalecam, aby nie zezwalać na domyślną nazwę klasy, ponieważ pokonuje ona cel. jeśli chcemy, aby nazwa klasy została nazwana, wymuś ją na każdej klasie. – YeenFei

+0

Tak, wystarczy. Niektórzy ludzie wolą pracować z dowolną klasą, nawet z innymi klasami. – Goz

+0

Podejrzewam, że chodziło Ci o #className zamiast ## className? – usta

13

użycie typeid(class).name

// illustratory kod zakładając wszystko includes/nazw itp

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

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

Ważne jest, aby pamiętać, że to daje nazwy wdrożeniowe zdefiniowane.

Z tego co wiem, nie ma możliwości wiarygodnego uzyskania nazwy obiektu w czasie wykonywania, np. "A" w twoim kodzie.

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: Jaki jest wynik, którego oczekujesz? Otrzymuję '1A', ale nazwa klasy to' one'. – Lazer

+0

@Lazer: Jak wskazano, drukuje łańcuch zdefiniowany w implementacji. Odwołaj moją edycję, jeśli chcesz mieć bardziej fantazyjny sposób. – Chubsdad

+1

Witryna [cppreference.com mówi] (http://en.cppreference.com/w/cpp/language/typeid#Notes) "Nie ma żadnej gwarancji, że ten sam std: : Instancja type_info zostanie zwrócona przez wszystkie oceny wyrażenia typu na tym samym typie. " Oznacza to, że 'i typeid (A) == & typeid (A)' może mieć wartość 'false'. W C++ 11 można użyć ['std :: type_index'] (http://en.cppreference.com/w/cpp/types/type_index). – JojOatXGME

7

Chcesz [classname], aby być 'jeden' i [ObjectName], aby być 'A'?

Jeśli tak, to nie jest możliwe. Te nazwy są tylko abstrakcjami dla programisty i nie są faktycznie używane w wygenerowanym kodzie binarnym. Możesz nadać klasie statyczną nazwę klasy zmiennej, którą ustawisz na "jeden" i normalną nazwę obiektu, którą możesz przypisać bezpośrednio, metodą lub konstruktorem. Następnie możesz zapytać o te metody dla nazw klas i obiektów.

+0

@Alexander: tak. – Lazer

+0

@Lazer: Mogę wiedzieć, dlaczego? – Chubsdad

+1

@chubsdad: spójrz na przykład kodu w pytaniu. – Lazer

2

Wystarczy napisać prosty szablon:

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

struct A {} a; 

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

Poprawa na @Chubsdad odpowiedź

//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(); 
} 

Które będzie drukować:

class A* 
6

Aby uzyskać nazwę klasy bez maglowania rzeczy co można Użyj makro func w konstruktorze:

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

specyficzną dla tego kompilatora. Jeden da ci 'MyClass :: MyClass' drugi' MyClass'. Gdy użyjemy go w zwykłej metodzie, stworzy on 'MyClass :: SomeMethod' lub' SomeMethod'. –

Powiązane problemy