fun(Base*): push rbp mov rbp, rsp mov QWORD PTR [rbp-24], rdi mov QWORD PTR [rbp-8], OFFSET FLAT:typeinfo for Base* pop rbp ret vtable for Derived: .quad 0 .quad typeinfo for Derived .quad Derived::fun() vtable for Base: .quad 0 .quad typeinfo for Base .quad Base::fun() typeinfo name for Base*: .string 'P4Base' typeinfo for Base*: .quad vtable for __cxxabiv1::__pointer_type_info+16 .quad typeinfo name for Base* .long 0 .zero 4 .quad typeinfo for Base typeinfo name for Derived: .string '7Derived' typeinfo for Derived: .quad vtable for __cxxabiv1::__si_class_type_info+16 .quad typeinfo name for Derived .quad typeinfo for Base typeinfo name for Base: .string '4Base' typeinfo for Base: .quad vtable for __cxxabiv1::__class_type_info+16 .quad typeinfo name for Base
首先,我们看fun()函数的汇编(fun(Base*):处),在其中有一行OFFSET FLAT:typeinfo for Base* 代表获取Base指针所指向对象的typeinfo。那么typeinfo又是如何获取的呢?
我们以Base指针实际指向Derived对象为例,vtable for Derived:部分代表着Derived类的虚函数表内容,其中有一行typeinfo for Derived代表着Derived类的typeinfo信息,而在该段中有一句typeinfo name for Derived代表着该类的名称(7Derived经过mangle之后,该句在上述代码中可以找到)。
main: push rbp mov rbp, rsp mov QWORD PTR [rbp-8], OFFSET FLAT:typeinfo for MyClss mov eax, 0 pop rbp ret typeinfo name for MyClss: .string '6MyClss' typeinfo for MyClss: .quad vtable for __cxxabiv1::__class_type_info+16 .quad typeinfo name for MyClss
// __dynamic_cast声明 __dynamic_cast (constvoid *src_ptr, // object started from const __class_type_info *src_type, // type of the starting object const __class_type_info *dst_type, // desired target type ptrdiff_t src2dst) // how src and dst are related
所以,有没有可能__dynamic_cast只是dynamic_cast的一个分支实现?
为了验证猜测,示例如下:
#include<iostream> #include<typeinfo>
classBase1 { public: voidf0() {} virtualvoidf1() {} int a; };
classBase2 { public: virtualvoidf2() {} int b; };
classDerived : public Base1, public Base2 { public: voidd() {} voidf2() {} // override Base2::f2() int c; };
template <classT> intCheckType(T t) { int n = 0; if (dynamic_cast<Derived*>(t)) { n |= 1; } if (dynamic_cast<Base1*>(t)) { n |= 2; } if (dynamic_cast<Base2*>(t)) { n |= 4; } return n; }
intmain() { Derived *d = new Derived; Base1 *b1 = new Base1; Base2 *b2 = new Base2; CheckType(d); CheckType(b1); CheckType(b2); return0; }
0x0000000000400a95 <+133>: mov $0x0,%eax 0x0000000000400a9a <+138>: test %al,%al 0x0000000000400a9c <+140>: je 0x400aa2 <_Z9CheckTypeIP5Base1EiT_+146> 0x0000000000400a9e <+142>: orl $0x4,-0x4(%rbp)
0x0000000000400aa2 <+146>: mov -0x4(%rbp),%eax 0x0000000000400aa5 <+149>: leaveq ---Type <return> to continue, or q <return> to quit--- 0x0000000000400aa6 <+150>: retq End of assembler dump.
__dynamic_cast (constvoid *src_ptr, // object started from const __class_type_info *src_type, // type of the starting object const __class_type_info *dst_type, // desired target type ptrdiff_t src2dst) // how src and dst are related
在上述声明中:
· src_ptr代表需要转换的指针
· src_type原始类型
· dst_type目标类型
· src2dst表示从dst到src的偏移量,当该值为如下3个之一时候,有特殊含义:
· -1: no hint
· -2: src is not a public base of dst
· -3: src is a multiple public base type but never a virtual base type
extern'C'void * __dynamic_cast (constvoid *src_ptr, // object started from const __class_type_info *src_type, // type of the starting object const __class_type_info *dst_type, // desired target type ptrdiff_t src2dst) // how src and dst are related { constvoid *vtable = *static_cast <constvoid *const *> (src_ptr); const vtable_prefix *prefix = adjust_pointer <vtable_prefix> (vtable, -offsetof (vtable_prefix, origin)); constvoid *whole_ptr = adjust_pointer <void> (src_ptr, prefix->whole_object); const __class_type_info *whole_type = prefix->whole_type; __class_type_info::__dyncast_result result;
// If the whole object vptr doesn't refer to the whole object type, we're // in the middle of constructing a primary base, and src is a separate // base. This has undefined behavior and we can't find anything outside // of the base we're actually constructing, so fail now rather than // segfault later trying to use a vbase offset that doesn't exist. constvoid *whole_vtable = *static_cast <constvoid *const *> (whole_ptr); const vtable_prefix *whole_prefix = adjust_pointer <vtable_prefix> (whole_vtable, -offsetof (vtable_prefix, origin)); constvoid *whole_vtable = *static_cast <constvoid *const *> (whole_ptr); const vtable_prefix *whole_prefix = (adjust_pointer <vtable_prefix> (whole_vtable, -ptrdiff_t (offsetof (vtable_prefix, origin)))); if (whole_prefix->whole_type != whole_type) returnNULL;
// Avoid virtual function call in the simple success case. if (src2dst >= 0 && src2dst == -prefix->whole_object && *whole_type == *dst_type) returnconst_cast <void *> (whole_ptr);
structvtable_prefix { // Offset to most derived object. ptrdiff_t whole_object; // Pointer to most derived type_info. const __class_type_info *whole_type; // What a class's vptr points to. constvoid *origin; };
· whole_object 表示当前指针指向对象的偏移量
· whole_type 指向 C++ 对象的类型:class(基类)、si_class(单一继承类型)、vmi_class(多重或虚拟继承类型)
struct__class_type_info::__dyncast_result { constvoid *dst_ptr; // pointer to target object or NULL __sub_kind whole2dst; // path from most derived object to target __sub_kind whole2src; // path from most derived object to sub object __sub_kind dst2src; // path from target to sub object int whole_details; // details of the whole class hierarchy ...