文章目录
- 建议
- 多重继承
- 虚函数表
- 虚函数表的存放位置,二进制文件
建议
需要看《深入理解C++对象模型》 这本书。
多重继承
多重继承就不应该存在,因为严重违反“简单原则”;不易理解,不易维护,不易开发。
就C++ Primer plus写到的一个例子说,一个工人衍生出歌唱家,和餐厅服务员,而如果一个工人既可以是歌唱家也可以是一个餐厅服务员。最后形成了一个会唱歌的餐厅服务员。其实是有违于类的一般通识。比如人属于动物,猪也属于动物,不能有一个动物既是人又是猪,虽然我们可以形容一个人如猪。
所以就工人衍生出歌唱家及餐厅服务员的例子,其实是工人作为一个实体,具有的技能是歌唱与服务。这一就好理解,工人可以拥有歌唱的技能,也可以拥有服务的技能。
本来就是不合理概念,大家还在试图讲解,而且讲解的还头头是道,这就造成学员的概念容易犯迷糊。
class istream : virtual public ios { … };
class ostream : virtual public ios { … }; 即使是虚继承多次基类,但是虚基类在对象内存布局里只出现一次。
In the case of virtual inheritance, only a single occurrence of the base class is maintained (called a subobject) regardless of how many times the class is derived from within the inheritance chain. iostream, for example, contains only a single instance of the virtual ios base class.
class A
{virtual void fun1(){};int a;
};class B
{virtual void fun1(){};int b;
};class D: virtual A
{virtual void fun1(){};int d;
};class E: virtual A
{virtual void fun1(){};int e;
};class F: A,B
{virtual void fun1(){};int f;
};
class G: D,E
{virtual void fun1(){};int g;
};int main()
{
F f;
G g;
return 0;
}(gdb) p f
$1 = {<A> = {_vptr.A = 0x400b98 <vtable for F+16>,a = 4195984},<B> = {_vptr.B = 0x400bb0 <vtable for F+40>,b = 0},members of F:f = 0
}
(gdb) p &(f.a)
$2 = (int *) 0x7fffffffe338
(gdb) p &(f.b)
$3 = (int *) 0x7fffffffe348
(gdb) p &(f.f)
$4 = (int *) 0x7fffffffe34c(gdb) p g
$5 = {<D> = {<A> = {_vptr.A = 0x400ac8 <vtable for G+88>,a = 0},members of D:_vptr.D = 0x400a88 <vtable for G+24>,d = 4196334},<E> = {members of E:_vptr.E = 0x400aa8 <vtable for G+56>,e = 4196893},members of G:g = 0
}(gdb) p &(g.a)
$6 = (int *) 0x7fffffffe328
(gdb) p &(g.d)
$7 = (int *) 0x7fffffffe308
(gdb) p &(g.e)
$8 = (int *) 0x7fffffffe318
(gdb) p &(g.g)
$9 = (int *) 0x7fffffffe31c如果类继承不加virtual,
会有两份A出现
(gdb) p g
$2 = {<D> = {<A> = {_vptr.A = 0x400a00 <vtable for G+16>,a = 4196334},members of D:d = 0},<E> = {<A> = {_vptr.A = 0x400a18 <vtable for G+40>,a = 4196765},members of E:e = 0},members of G:g = -136447344
虚函数表
看着网上的有些个博客写的也不太准确;
虚函数表指针,是虚基类的一个成员,是一个指针;使用gdb看时,这个指针被归到了基类块。 这个指针放在类定义的起始位置。对象的起始位置。
所以派生类对象的内存布局是:
SubClass
{BaseClass1{vtable *;BaseClass members;按照类定义里的顺序}SubClass members;
}class A
{virtual void fun1(){};int a;int b;
};class B
{virtual void fun1(){};int a;int b;
};
class C:A,B
{virtual void fun1(){};int c;int d;
};(gdb) p *c
$2 = {<A> = {_vptr.A = 0x4026b8 <vtable for C+16>, 虚函数表在 C 类定义的这个位置 如果又两个 相同的虚函数在两个基类里,其实用的是第一个的。a = 0,b = 0},<B> = {_vptr.B = 0x4026d0 <vtable for C+40>, 虚函数表在C 类定义的这个位置a = 0,b = 0},members of C:c = 0,d = 0
}这里,gdb自动将虚函数指针指向了函数表的函数地址表的位置,将运行时的类型给跳过了。起始在函数指针列表之前还有运行时类型的信息。
虚函数表的存放位置,二进制文件
是build到了 .rodata段。
[13] .rodata PROGBITS 00141a20 141a20 019b5c 00 A 0 0 32
(gdb) p *this
$5 = {<B> = {<A> = {_vptr.A = 0xf7772d88 <vtable for C+8>,(gdb) x /16x 0xf7772d88
0xf7772d88 <_ZTV10C+8>: 0xf7725aca 0xf7725ae4 0xf7722a72 0xf770fc1e
0xf7772d98 <_ZTV10C+24>: 0xf7711772 0xf771195c 0x00000000 0xf7772c98(gdb) disass 0xf7725ae4
Dump of assembler code for function _ZN10CD0Ev:0xf7725ae4 <+0>: push %ebp0xf7725ae5 <+1>: mov %esp,%ebp
(gdb) disass 0xf7722a72
Dump of assembler code for function _ZN10C19C0EPci:0xf7722a72 <+0>: push %ebp0xf7722a73 <+1>: mov %esp,%ebp0xf7722a75 <+3>: push %edi0xf7722a76 <+4>: push %esi0xf7722a77 <+5>: push %ebx0xf7622000 0xf77a6000 0x184000 0x0 /home/lib/liba.so0xf77a6000 0xf77a7000 0x1000 0x183000 /home/lib/liba.so0xf77a7000 0xf77a8000 0x1000 0x184000 /home/lib/liba.so# objdump -x /export/home/lss/lib/liba.so | grep 100a72
00100a72 g F .text 00002e91 _ZN10C190CEPci(gdb) p /x 0xf7722a72-0x100a72
$6 = 0xf7622000
(gdb) p /x 0xf7772d88 - 0xf7622000
$7 = 0x150d88
(gdb) p /x 0x150d88 - 00141a20
Invalid number "00141a20".
(gdb) p /x 0x150d88 - 0x141a20
$8 = 0xf368[13] .rodata PROGBITS 00141a20 141a20 019b5c 00 A 0 0 32objdump -s --section=.rodata /home/lib/liba.so > /tmp/rem.out1150d70 00000000 00000000 00000000 00000000 ................150d80 00000000 00000000 00000000 00000000 ................ /// 这个是系统load so之后形成的一个表?150d90 00000000 00000000 00000000 00000000 ................150da0 00000000 00000000 00000000 00000000 ................150db0 00000000 00000000 00000000 00000000 ................