Hãy tưởng tượng mỗi lớp như thể nó có phương pháp ảo này, nhưng chỉ khi nó đã có một ảo, và một đối tượng khác được tạo ra cho mỗi loại:
extern std::type_info __Example_info;
struct Example {
virtual std::type_info const& __typeid() const {
return __Example_info;
}
};
// "__" used to create reserved names in this pseudo-implementation
Sau đó tưởng tượng bất kỳ sử dụng typeid trên một đối tượng, typeid(obj)
, trở thành obj.__typeid()
. Sử dụng trên con trỏ tương tự trở thành pointer->__typeid()
. Ngoại trừ sử dụng trên con trỏ null (mà ném bad_typeid), trường hợp con trỏ là giống hệt với trường hợp không phải con trỏ sau khi dereferencing, và tôi sẽ không đề cập đến nó thêm nữa. Khi áp dụng trực tiếp trên một loại, hãy tưởng tượng rằng trình biên dịch chèn một tham chiếu trực tiếp vào đối tượng được yêu cầu: typeid(Example)
trở thành __Example_info
.
Nếu một lớp không có RTTI (tức là nó không có virtuals, ví dụ như NoRTTI bên dưới), sau đó tưởng tượng nó với một giống __typeid phương pháp đó là không ảo. Điều này cho phép chuyển đổi tương tự thành các cuộc gọi phương thức như trên, dựa vào công văn ảo hoặc phi ảo của các phương thức đó, nếu thích hợp; nó cũng cho phép một số lời gọi phương thức ảo được chuyển thành công văn không ảo, vì có thể được thực hiện cho bất kỳ phương thức ảo nào.
struct NoRTTI {}; // a hierarchy can mix RTTI and no-RTTI, just as use of
// virtual methods can be in a derived class even if the base
// doesn't contain any
struct A : NoRTTI { virtual ~A(); }; // one virtual required for RTTI
struct B : A {}; // ~B is virtual through inheritance
void typeid_with_rtti(A &a, B &b) {
typeid(a); typeid(b);
A local_a; // no RTTI required: typeid(local_a);
B local_b; // no RTTI required: typeid(local_b);
A &ref = local_b;
// no RTTI required, if the compiler is smart enough: typeid(ref)
}
Ở đây, typeid phải sử dụng RTTI cho cả hai tham số (B có thể là một lớp cơ sở cho một loại mới hơn), nhưng không cần RTTI cho một trong hai biến cục bộ vì kiểu động (hoặc "loại runtime") hoàn toàn được biết đến. Điều này phù hợp, không phải ngẫu nhiên, làm thế nào các cuộc gọi ảo có thể tránh công văn ảo.
struct StillNoRTTI : NoRTTI {};
void typeid_without_rtti(NoRTTI &obj) {
typeid(obj);
StillNoRTTI derived; typeid(derived);
NoRTTI &ref = derived; typeid(ref);
// typeid on types never uses RTTI:
typeid(A); typeid(B); typeid(NoRTTI); typeid(StillNoRTTI);
}
Ở đây, sử dụng ở hai obj hoặc ref sẽ tương ứng với NoRTTI! này đúng ngay cả mặc dù trước đây có thể của một lớp dẫn xuất (obj có thể thực sự là một thể hiện của Một hoặc B) và mặc dù ref chắc chắn của một lớp được thừa kế là. Tất cả các ứng dụng khác (dòng cuối cùng của hàm) cũng sẽ được giải quyết tĩnh.
Lưu ý rằng trong các hàm mẫu này, mỗi loại typeid sử dụng RTTI hoặc không phải như tên hàm ngụ ý. (Do đó việc sử dụng nhận xét trong with_rtti.)
Đối tượng C++ không bao giờ có kích thước bằng 0 (ngoại trừ đối tượng phụ lớp cơ sở đó có thể). Tuy nhiên, điều này không phải vì 'typeid', bởi vì nếu bạn tạo ra một mảng có thứ gì đó bằng 0, tất cả các đối tượng sẽ ở cùng một địa chỉ. Nó hữu ích như là một tài sản của ngôn ngữ mà các đối tượng riêng biệt từng có địa chỉ riêng của họ, do đó không có đối tượng kích thước bằng không. Điều này không quan trọng lắm đối với các đối tượng phụ lớp cơ sở, nếu chỉ vì mọi cái được sử dụng cho ý tưởng rằng một số lớp cơ sở của cùng một đối tượng có thể ở cùng một địa chỉ - đó là điều thường xảy ra trong thừa kế đơn. –