How does typeid work and how do objects store class information?

http://en.wikipedia.org/wiki/Typeid

This seems like a mystery to me: how does the compiler store information about the type of an object? In principle, an empty class once created does not have zero size in memory.

+7
source share
4 answers

How it is stored is determined by the implementation. There are many different ways to do this.

However, for non-polymorphic types, nothing needs to be saved. For non-polymorphic types, typeid returns information about the static type of the expression, i.e. Its type of compilation time. The type is always known at compile time, so there is no need to associate any additional information with certain objects (just like for sizeof to work, you really do not need to store the size of the object anywhere). The "empty object" that you specify in your question will be an object of a non-polymorphic type, so there is no need to store anything in it, and there is no problem with its zero size. (Meanwhile, polymorphic objects are never โ€œemptyโ€ and never have โ€œzero size in memory.โ€)

For polymorphic types, typeid does return information about the dynamic type of the expression, i.e. about its type of runtime. To implement this, something must be stored inside the real object at runtime. As I said before, different compilers implement it differently. In MSVC ++, for example, the VMT pointer stored in each polymorphic object indicates a data structure that contains the so-called RTTI information about the runtime of the object, in addition to the actual VMT.

The fact that you mention zero-sized objects in your question probably indicates that you have some misconceptions about what typeid can and cannot do. Remember that typeid can determine the actual (i.e. dynamic) type of an object for polymorphic types only. For non-polymorphic types, typeid cannot determine the actual type of the object and returns to the primitive compilation functionality.

+10
source

Imagine each class as if it had this virtual method, but only if it already has one other virtual object, and one object is created for each type:

 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 

Then imagine that any use of typeid for a typeid(obj) object becomes obj.__typeid() . Similarly, using in pointers becomes pointer->__typeid() . With the exception of using null pointers (which calls bad_typeid), the case of a pointer is identical to the case without a pointer after dereferencing, and I will not mention it further. When applied directly to a type, imagine that the compiler inserts a link directly to the required object: typeid(Example) becomes __Example_info .

If the class does not have RTTI (i.e. it does not have virtual machines, for example NoRTTI below), then imagine it using the identical __ typeid method, which is not virtual. This allows a similar conversion to method calls, as described above, depending on the virtual or non-virtual sending of these methods; it also allows you to turn invocations of virtual methods into non-virtual dispatch, as can be done for any virtual method.

 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) } 

Here, typeid must use RTTI for both parameters (B may be the base class for the later type), but RTTI is not needed for the local variable, because the dynamic type (or "run-time type") is absolutely known. This coincidence is no coincidence how virtual calls can avoid virtual sending.

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

Here using either obj or ref will match NoRTTI! This is true, although the former may have a derived class (obj may indeed be an instance of A or B), and although ref is definitely a derived class. All other functions (the last line of the function) will also be allowed statically.

Note that in these example functions, each typeid uses RTTI or not, as the name of the function implies. (Consequently, with_rtti is used in the comments.)

+6
source

Even if you are not using type information, an empty class will not have null bytes, it always has something if I remember the standard requirements correctly.

I believe that typeid is implemented similarly to the vtable pointer, the object will have a "hidden" pointer to its typeid.

+1
source

There are several questions in one question.

In C ++, objects are what takes up memory. If it does not occupy any memory, this is not an object (although a sub-object of the base class may not take up space). Thus, the object must occupy at least 1 byte.

The compiler does not save type information if your class does not have a virtual function. In this case, a pointer to the type of information is often stored with a negative offset in the virtual function table. Please note that the standard does not mention any virtual tables or the type of information format, therefore it is a purely implementation detail.

0
source

All Articles