2010-09-06 24 views
32

Có thể lấy tên đối tượng không?Làm cách nào để lấy tên lớp từ đối tượng 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"; 
} 

nơi tôi có thể lấy tên, một cái gì đó giống như

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

Liệu C++ cung cấp bất kỳ cơ chế để đạt được điều này?

+0

Tôi đang bối rối. Bạn có liên quan đến C++ 'class' với một" lớp học sinh "không? Nếu bạn có một 'lớp' đại diện cho một lớp, và lớp đó có tên như" Mẫu giáo của bà Gutentag ", nó phải có một thành viên dữ liệu để lưu trữ nó như là một' std :: string'. – Potatoswatter

+0

@Potatoswatter: Bây giờ, tôi bối rối. Bạn hỏi gì? – Lazer

+1

Có hai nghĩa của từ "class" và loại lớp có sinh viên không phải là từ khóa 'class' đề cập đến. – Potatoswatter

Trả lời

51

Bạn có thể hiển thị tên biến bằng cách sử dụng bộ tiền xử lý. Ví dụ

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

đầu ra

3one A 

trên máy tính của tôi. Các # thay đổi một mã thông báo vào một chuỗi, sau khi tiền xử lý dòng là

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

Tất nhiên nếu bạn làm điều gì đó như

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

bạn sẽ nhận được

3one B 

như trình biên dịch doesn' t theo dõi tất cả các tên của biến.

Vì nó xảy ra trong gcc kết quả của typeid(). Name() là tên lớp đọc sai, để có được những demangled version sử dụng

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

mà mang lại cho tôi

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

trình biên dịch khác có thể sử dụng các lược đồ đặt tên khác nhau.

+5

Tại sao chúng ta nhận được '3one'? '3' là gì? – Lazer

+1

@Lazer Không có ý tưởng, như mọi người nói rằng kết quả của 'typeid' là nền tảng cụ thể. Nó có thể là một cái gì đó để làm với chương trình mangling tên của C++. –

+1

Nếu tôi thử 'template class one {};' và sử dụng 'one ' Tôi nhận '3oneIiE' làm tên lớp, vì vậy nó thực sự liên quan đến tên mangling (Có thể là lớp có' 3' tên ký tự 'một' với' I' một đối số mẫu 'i' (int)' E' kết thúc đối số) –

4

Bạn có thể thử sử dụng "typeid".

Điều này không hoạt động đối với tên "đối tượng" nhưng bạn biết tên đối tượng để bạn chỉ phải lưu trữ ở đâu đó. Trình biên dịch không quan tâm đến những gì bạn đã đặt tên cho một đối tượng. Tuy nhiên,

Mặc dù vậy, đầu ra của typeid là một trình biên dịch cụ thể nên ngay cả khi nó tạo ra những gì bạn đang có trên nền tảng hiện tại, nó có thể không xuất hiện trên nền tảng khác. Điều này có thể hoặc không có thể là một vấn đề cho bạn.

Giải pháp khác là tạo một số loại trình bao bọc mẫu mà bạn lưu trữ tên lớp. Sau đó, bạn cần sử dụng chuyên môn từng phần để làm cho nó trả lại tên lớp chính xác cho bạn. Điều này có lợi thế về thời gian biên dịch làm việc nhưng phức tạp hơn nhiều.

Chỉnh sửa: Là rõ ràng hơn

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

Sau đó cho mỗi somethign lớp liek sau:

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

Mà thậm chí có thể được macro'd như sau:

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

phép bạn, chỉ cần làm,

DefineClassName(MyClass); 

Tính năng cuối cùng Lấy tên lớp bạn muốn làm như sau:

ClassName<MyClass>::name(); 

Edit2: Xây dựng thêm sau đó bạn sẽ cần phải đặt này "DefineClassName" vĩ mô trong mỗi lớp bạn thực hiện và xác định một " classname "function sẽ gọi hàm tĩnh template.

Edit3: Và suy nghĩ về nó ... gửi bài điều đầu tiên rõ ràng là xấu của nó vào buổi sáng khi bạn có thể cũng chỉ định nghĩa một hàm thành viên "classname()" như sau:

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

có thể được macro'd như sau:

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

sau đó, bạn có thể đơn giản chỉ cần thả

DefineClassName(MyClass); 

vào cl ass như bạn xác định nó ...

+0

tôi khuyên bạn không nên cho phép tên lớp mặc định vì nó đánh bại mục đích. nếu chúng ta muốn lớp được đặt tên, sau đó thực thi nó trên mọi lớp. – YeenFei

+0

Đủ công bằng. Một số người có thể thích nó làm việc với bất kỳ lớp học nào thậm chí các lớp khác. – Goz

+0

Tôi đoán bạn có nghĩa là #className thay vì ## className? – usta

13

sử dụng typeid(class).name

// đang illustratory giả định tất cả bao gồm/không gian tên vv

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

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

Điều quan trọng là hãy nhớ rằng này đưa ra một định nghĩa thực hiện tên.

Theo như tôi biết, không có cách nào để nhận tên đối tượng trong thời gian chạy đáng tin cậy, ví dụ: 'A' trong mã của bạn.

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: Sản lượng mà bạn mong đợi là gì? Tôi nhận được '1A', nhưng tên lớp là' một'. – Lazer

+0

@Lazer: Như đã chỉ ra, điều này in một chuỗi được xác định thực hiện. Tham khảo bản chỉnh sửa của tôi nếu bạn muốn một cách ưa thích hơn – Chubsdad

+1

Trang web [cppreference.com nói] (http://en.cppreference.com/w/cpp/language/typeid#Notes) "Không có gì đảm bảo rằng cùng một tiêu chuẩn: : thể loại type_info sẽ được trả về bởi tất cả các đánh giá của biểu thức typeid trên cùng một loại. " Điều này có nghĩa là '& typeid (A) == & typeid (A)' có thể là 'false'. Với C++ 11 bạn có thể sử dụng ['std :: type_index'] (http://en.cppreference.com/w/cpp/types/type_index). – JojOatXGME

7

Bạn có muốn [classname] là 'một' và [ObjectName] là 'A'?

Nếu vậy, điều này là không thể. Những tên này chỉ là trừu tượng cho lập trình viên, và không thực sự được sử dụng trong mã nhị phân được tạo ra. Bạn có thể cung cấp cho lớp một tên lớp biến tĩnh, mà bạn đặt thành 'một' và một tên đối tượng biến bình thường mà bạn sẽ gán trực tiếp, thông qua một phương thức hoặc hàm tạo. Sau đó bạn có thể truy vấn các phương thức này cho tên lớp và đối tượng.

+0

@Alexander: có. – Lazer

+0

@Lazer: Tôi có thể biết tại sao không? – Chubsdad

+1

@chubsdad: Hãy xem ví dụ về mã trong câu hỏi. – Lazer

2

Chỉ cần viết mẫu đơn giản:

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

struct A {} a; 

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

Một cải tiến cho @Chubsdad câu trả lời,

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

nào sẽ in:

class A* 
6

Để có được tên lớp mà không mangling thứ bạn có thể sử dụng func macro trong hàm tạo:

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

trình biên dịch si này cụ thể. Một sẽ cung cấp cho bạn 'MyClass :: MyClass' khác' MyClass'. Khi sử dụng phương thức thông thường, nó sẽ tạo 'MyClass :: SomeMethod' hoặc' SomeMethod'. –

Các vấn đề liên quan