不同的数据在内存中有不同的保存时机、保存位置。在执行程序时,总内存会被分为多个段,称为文本,未初始化的全局、初始化的全局、栈和堆段。将整个程序加载到文本段中,并在堆栈存储器中选择存储器到变量。
High Addresses ---> .----------------------.
| Environment |
|----------------------|
| | Functions and variable are declared
| STACK | on the stack.
base pointer -> | - - - - - - - - - - -|
| | |
| v |
: :
. . The stack grows down into unused space
. Empty . while the heap grows up.
. .
. . (other memory maps do occur here, such
. . as dynamic libraries, and different memory
: : allocate)
| ^ |
| | |
brk point -> | - - - - - - - - - - -| Dynamic memory is declared on the heap
| HEAP |
| |
|----------------------|
| BSS | Uninitialized data (BSS)
|----------------------|
| Data | Initialized data (DS)
|----------------------|
| Text | Binary code
Low Addresses ----> '----------------------'
此段包含所有未初始化的全局和静态变量,所有变量都由零或者空指针初始化。程序加载器在加载程序时为BSS节分配内存。
此段包含显式初始化的全局变量和静态变量,大小由程序源代码中值的大小决定,在运行时不会更改。它具有读写权限,因此可以在运行时更改此段的变量值。
该段是一个只读段,包含已编译程序的二进制文件。该段是可共享的,因此对于文本编辑器等频繁执行的程序,内存中只需要一个副本
#include
class A
{
public:
int a;
A() : a(0x1) {}
virtual void foo() { std::cout << "A::foo()" << std::endl; }
void bar() { std::cout << "A::bar()" << std::endl; }
};
class B : public A
{
public:
int b;
B() : A(), b(0x2) {}
void foo() { std::cout << "B::foo()" << std::endl; }
};
class C : public B
{
public:
int c;
C() : B(), c(0x3) {}
void foo() { std::cout << "C::foo()" << std::endl; }
};
int main(int argc, char **argv)
{
A a;
B b;
C c;
B *p = &c;
p->foo();
std::cout << sizeof(int) << " " << sizeof(int *) << std::endl;
return 0;
}
g++ main.cpp -o main -std=c++14 -g
gdb查看
(gdb) b 29
Breakpoint 1 at 0x120b: file main.cpp, line 29.
(gdb) r
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:29
29 A a;
(gdb) n
30 B b;
(gdb) set print pretty on
(gdb) set print vtbl on
(gdb) p a
$1 = {
_vptr.A = 0x555555557d38 <vtable for A+16>,
a = 1
}
(gdb) p/a &a
$2 = 0x7fffffffdbf0
(gdb) p/a &a.a
$3 = 0x7fffffffdbf8
(gdb) p sizeof(a)
$4 = 16
(gdb) x/2xg &a
0x7fffffffdbf0: 0x0000555555557d38 0x00007fff00000001
(gdb) info vtbl a
vtable for 'A' @ 0x555555557d38 (subobject @ 0x7fffffffdbf0):
[0]: 0x555555555344 <A::foo()>
gdb查看
(gdb) n
31 C c;
(gdb) p b
$6 = {
<A> = {
_vptr.A = 0x555555557d20 <vtable for B+16>,
a = 1
},
members of B:
b = 2
}
(gdb) p sizeof(b)
$7 = 16
(gdb) n
32 B *p = &c;
(gdb) p c
$8 = {
<B> = {
<A> = {
_vptr.A = 0x555555557d08 <vtable for C+16>,
a = 1
},
members of B:
b = 2
},
members of C:
c = 3
}
(gdb) p sizeof(c)
$9 = 24
(gdb) info vtbl b
vtable for 'B' @ 0x555555557d20 (subobject @ 0x7fffffffdc00):
[0]: 0x5555555553ba <B::foo()>
(gdb) info vtbl c
vtable for 'C' @ 0x555555557d08 (subobject @ 0x7fffffffdc10):
[0]: 0x555555555430 <C::foo()>
如果class B中申明了新的虚函数(比如foo2),class B中依然只有一个虚函数表,只不过会把foo2加入到该表中。此时class A的虚函数表不会包含foo2。
#include
class A
{
int a;
virtual void foo() { std::cout << "A::foo()" << std::endl; }
};
class B
{
int b;
virtual void bar() { std::cout << "B::bar()" << std::endl; }
};
class C : public A, public B
{
int c;
void foo() { std::cout << "C::foo()" << std::endl; }
void bar() { std::cout << "C::bar()" << std::endl; }
};
int main(int argc, char **argv)
{
A a;
B b;
C c;
std::cout << sizeof(int) << " " << sizeof(int *) << std::endl;
return 0;
}
gdb查看
gdb main
(gdb) b 28
Breakpoint 1 at 0x122f: file main.cpp, line 28.
(gdb) set print pretty on
(gdb) set print vtbl on
(gdb) set print object on
(gdb) p a
No symbol "a" in current context.
(gdb) r
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:28
28 std::cout << sizeof(int) << " " << sizeof(int *) << std::endl;
(gdb) p a
$1 = (A) {
_vptr.A = 0x555555557d20 <vtable for A+16>,
a = -134528544
}
(gdb) p b
$2 = (B) {
_vptr.B = 0x555555557d08 <vtable for B+16>,
b = -135408993
}
(gdb) p/a c
$3 = (C) {
<A> = {
_vptr.A = 0x555555557cd0 <vtable for C+16>,
a = 0xfffffffff7eab60a
},
<B> = {
_vptr.B = 0x555555557cf0 <vtable for C+48>,
b = 0xfffffffff7fb3e88
},
members of C:
c = 0x7fff
--Type <RET> for more, q to quit, c to continue without paging--
}
(gdb) p sizeof(c)
$4 = 32
(gdb) x/5ag &c
0x7fffffffdc00: 0x555555557cd0 <_ZTV1C+16> 0x7ffff7eab60a <_ZNSt9basic_iosIwSt11char_traitsIwEE15_M_cache_localeERKSt6locale+90>
0x7fffffffdc10: 0x555555557cf0 <_ZTV1C+48> 0x7ffff7fb3e88 <_ZSt5wclog+8>
0x7fffffffdc20: 0x7ffff7fb3940
#include
class A
{
int a;
virtual void foo() { std::cout << "A::foo()" << std::endl; }
};
class B : virtual public A
{
int b;
virtual void foo() { std::cout << "B::foo()" << std::endl; }
};
class C : virtual public A
{
int c;
void foo() { std::cout << "C::foo()" << std::endl; }
};
class D : public B, public C
{
int d;
virtual void foo() { std::cout << "D::foo()" << std::endl; }
};
int main(int argc, char **argv)
{
A a;
B b;
C c;
D d;
A *pa = &d;
B *pb = &d;
C *p c = &d;
std::cout << sizeof(int) << " " << sizeof(int *) << std::endl;
return 0;
}
gdb查看
gdb main
(gdb) b 37
Breakpoint 1 at 0x1247: file main.cpp, line 37.
(gdb) r
37 std::cout << sizeof(int) << " " << sizeof(int *) << std::endl;
(gdb) set print pretty on
(gdb) set print object on
(gdb) set print vtbl on
(gdb) p a
$1 = (A) {
_vptr.A = 0x555555557ce0 ,
a = 0
}
(gdb) p b
$2 = (B) {
= {
_vptr.A = 0x555555557cb8 ,
a = -238370304
},
members of B:
_vptr.B = 0x555555557c98 ,
b = 0
--Type for more, q to quit, c to continue without paging--
}
(gdb) p c
$3 = (C) {
= {
_vptr.A = 0x555555557c68 ,
a = -134528544
},
members of C:
_vptr.C = 0x555555557c48 ,
c = -134529192
}
(gdb) p d
$4 = (D) {
= {
= {
_vptr.A = 0x555555557b70 ,
a = -134529400
},
members of B:
_vptr.B = 0x555555557b30 ,
b = -135408993
},
= {
members of C:
_vptr.C = 0x555555557b50 ,
c = -135612918
},
members of D:
d = 32767
}
(gdb) p &d
$5 = (D *) 0x7fffffffdbf0
class A
{
public:
int m_a;
int m_b;
static int m_c; // 声明
}
int A::m_c = 0; // 定义
#include
#include
class Base
{
public:
int m_a;
int m_b;
};
class Derived : public Base
{
public:
int m_i;
int m_j;
};
int main(int argc, char **argv)
{
// 打印成员变量偏移值
printf("Base::m_a = %d\n", &Base::m_a);
printf("Base::m_b = %d\n", &Base::m_b);
printf("Derived::m_a = %d\n", &Derived::m_a);
printf("Derived::m_b = %d\n", &Derived::m_b);
printf("Derived::m_i = %d\n", &Derived::m_i);
printf("Derived::m_j = %d\n", &Derived::m_j);
Base b; // m_a | m_b
Derived d; // m_a | m_b | m_i | m_j
return 0;
}
#include
#include
class Base1
{
public:
int m_a;
char m_c1;
};
class Base2 : public Base1
{
public:
char m_c2;
};
class Base3 : public Base2
{
public:
char m_c3;
};
int main(int argc, char **argv)
{
Base1 b1; // 8byte: 4 | 1 | 3padding
Base2 b2; // 12byte: 4 | 1 | 3padding | 1 | 3padding
Base3 b3; // 12byte: 4 | 1 | 3padding | 1 | 1 | 2padding
printf("sizeof(Base1) = %d\n", sizeof(Base1));
printf("sizeof(Base2) = %d\n", sizeof(Base2));
printf("sizeof(Base3) = %d\n", sizeof(Base3));
// 打印成员变量偏移值
printf("Base3::m_a = %d\n", &Base3::m_a);
printf("Base3::m_c1 = %d\n", &Base3::m_c1);
printf("Base3::m_c2 = %d\n", &Base3::m_c2);
printf("Base3::m_c3 = %d\n", &Base3::m_c3);
return 0;
}
输出结果
sizeof(Base1) = 8
sizeof(Base2) = 12
sizeof(Base3) = 12
Base3::m_a = 0
Base3::m_c1 = 4
Base3::m_c2 = 8
Base3::m_c3 = 9
类中引入虚函数时会有额外的成本付出:
class Base
{
public:
int m_i;
int m_j;
Base() {}
~Base(){}
virtual void func() {}
};
//内存布局为
// vptr | m_i | m_j
#include
#include
class Base
{
public:
int m_i;
Base() {}
~Base() {}
virtual void b_func() {}
};
class Derived : public Base
{
public:
int m_a;
int m_b;
Derived() {}
~Derived() {}
virtual void d_func() {}
};
int main(int argc, char **argv)
{
printf("sizeof(Base) = %d\n", sizeof(Base));
printf("sizeof(Derived) = %d\n", sizeof(Derived));
Base b;
Derived d;
printf("Base::m_i = %d\n", &Base::m_i);
printf("Derived::m_i = %d\n", &Derived::m_i);
printf("Derived::m_a = %d\n", &Derived::m_a);
printf("Derived::m_b = %d\n", &Derived::m_b);
return 0;
}
输出结果为
sizeof(Base) = 16
sizeof(Derived) = 24
Base::m_i = 8
Derived::m_i = 8
Derived::m_a = 12
Derived::m_b = 16
//内存布局为
//Base: vptr | m_i | padding4
//Derived: vptr | m_i | m_a | m_b | padding4
#include
#include
class Base
{
public:
int m_i;
Base() {}
~Base() {}
};
class Derived : public Base
{
public:
int m_a;
int m_b;
Derived() {}
~Derived() {}
virtual void d_func() {}
};
int main(int argc, char **argv)
{
printf("sizeof(Base) = %d\n", sizeof(Base));
printf("sizeof(Derived) = %d\n", sizeof(Derived));
Derived d;
d.m_i = 1;
d.m_a = 2;
d.m_b = 3;
printf("Base::m_i = %d\n", &Base::m_i);
printf("Derived::m_i = %d\n", &Derived::m_i);
printf("Derived::m_a = %d\n", &Derived::m_a);
printf("Derived::m_b = %d\n", &Derived::m_b);
return 0;
}
gdb查看
gdb main
(gdb) set print pretty on
(gdb) set print object on
(gdb) set print vtbl on
(gdb) b 35
Breakpoint 1 at 0x123f: file main.cpp, line 35.
(gdb) r
sizeof(Base) = 4
sizeof(Derived) = 24
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:35
35 printf("Base::m_i = %d\n", &Base::m_i);
(gdb) p/a &d
$1 = 0x7fffffffdc00
(gdb) x/10xw &d
0x7fffffffdc00: 0x55557d40 0x00005555 0x00000001 0x00000002
0x7fffffffdc10: 0x00000003 0x00007fff 0x342a4e00 0xa0066d24
0x7fffffffdc20: 0xf7fb3940 0x00007fff
(gdb) info vtbl d
vtable for 'Derived' @ 0x555555557d40 (subobject @ 0x7fffffffdc00):
[0]: 0x5555555553e4 <Derived::d_func()>
从gdb调试信息可以看出
d的vptr为0x555555557d40 即从虚拟地址0x7fffffffdc00~0x7fffffffdc07
地址0x7fffffffdc08~0x7fffffffdc0b的值为0x00000001即d.m_i的值
地址0x7fffffffdc0c~0x7fffffffdc0f的值为0x00000002即d.m_a的值
地址0x7fffffffdc10~0x7fffffffdc13的值为0x00000003即d.m_b的值
由此可得d的内存布局为:vptr | m_i | m_a | m_b | padding4
#include
#include
class Base1
{
public:
int m_b1;
Base1() { printf(" Base1::Base1() 的this指针是:%p\n", this); }
~Base1() {}
virtual void func_b1() {}
};
class Base2
{
public:
int m_b2;
Base2() { printf(" Base2::Base2() 的this指针是:%p\n", this); }
~Base2() {}
virtual void func_b2() {}
};
class Derived : public Base1, public Base2
{
public:
int m_a;
int m_b;
Derived()
{
printf("Derived::Derived() 的this指针是:%p\n", this);
}
~Derived() {}
virtual void d_func() {}
};
int main(int argc, char **argv)
{
printf("sizeof(Base1) = %d\n", sizeof(Base1));
printf("sizeof(Base2) = %d\n", sizeof(Base2));
printf("sizeof(Derived) = %d\n", sizeof(Derived));
Derived d;
d.m_b1 = 1;
d.m_b2 = 2;
d.m_a = 3;
d.m_b = 4;
printf("Base1::m_b1 = %d\n", &Base1::m_b1);
printf("Base2::m_b2 = %d\n", &Base2::m_b2);
printf("Derived::m_b1 = %d\n", &Derived::m_b1);
printf("Derived::m_b2 = %d\n", &Derived::m_b2);
printf("Derived::m_a = %d\n", &Derived::m_a);
printf("Derived::m_b = %d\n", &Derived::m_b);
return 0;
}
gdb查看
gdb main
(gdb) set print pretty on
(gdb) set print object on
(gdb) set print vtbl on
(gdb) b 51
Breakpoint 1 at 0x125f: file main.cpp, line 51.
(gdb) r
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
sizeof(Base1) = 16
sizeof(Base2) = 16
sizeof(Derived) = 40
Base1::Base1() 的this指针是:0x7fffffffdbf0
Base2::Base2() 的this指针是:0x7fffffffdc00
Derived::Derived() 的this指针是:0x7fffffffdbf0
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:51
51 printf("Base1::m_b1 = %d\n", &Base1::m_b1);
(gdb) p d
$1 = (Derived) {
<Base1> = {
_vptr.Base1 = 0x555555557cd0 <vtable for Derived+16>,
m_b1 = 1
},
<Base2> = {
_vptr.Base2 = 0x555555557cf0 <vtable for Derived+48>,
m_b2 = 2
},
members of Derived:
m_a = 3,
m_b = 4
}
(gdb) x/10xw &d
0x7fffffffdbf0: 0x55557cd0 0x00005555 0x00000001 0x00007fff
0x7fffffffdc00: 0x55557cf0 0x00005555 0x00000002 0x00000003
0x7fffffffdc10: 0x00000004 0x00007fff
(gdb) info vtbl d
vtable for 'Derived' @ 0x555555557cd0 (subobject @ 0x7fffffffdbf0):
[0]: 0x55555555540c <Base1::func_b1()>
[1]: 0x555555555576 <Derived::d_func()>
vtable for 'Base2' @ 0x555555557cf0 (subobject @ 0x7fffffffdc00):
[0]: 0x555555555476 <Base2::func_b2()>
从gdb调试信息可以看出
Base1的虚函数指针vptr1和继承Derived的虚函数指针vptr同在0x7fffffffdbf0处,即继承类和多个基类的第一个类共用一个vptr
地址0x7fffffffdbf0~0x7fffffffdbf7的值为0x0000555555557cd0即_vptr.Base1
地址0x7fffffffdbf8~0x7fffffffdbfb的值为0x00000001即m_b1的值
地址0x7fffffffdbfc~0x7fffffffdbff的值为4字节的填充
地址0x7fffffffdc00~0x7fffffffdc07的值为0x0000555555557cf0即_vptr.Base2
地址0x7fffffffdc08~0x7fffffffdc0b的值为0x00000002即m_b2的值
地址0x7fffffffdc0c~0x7fffffffdc0f的值为0x00000003即m_a的值
地址0x7fffffffdc10~0x7fffffffdc13的值为0x00000004即m_b的值
由此可得d的内存布局为:_vptr.Base1 | m_b1 | 4padding |_vptr.Base2 | m_b2 | m_a | m_b | padding4
传统多重继承的问题:空间问题、效率问题、二义性问题
|-----------| |-----------|
| Base | | Base |
|-----------| |-----------|
| |
\|/ \|/
|----------| |----------|
| Derived1 | | Derived2 |
|----------| |----------|
| |
|---------------|
|
\|/
|----------|
| C |
|----------|
#include
#include
class Base
{
public:
int m_b1;
};
class Derived1 : public Base
{
public:
};
class Derived2 : public Base
{
public:
};
class C : public Derived1, public Derived2
{
};
int main(int argc, char **argv)
{
printf("sizeof(Base) = %ld\n", sizeof(Base));
printf("sizeof(Derived1) = %ld\n", sizeof(Derived1));
printf("sizeof(Derived2) = %ld\n", sizeof(Derived2));
printf("sizeof(C) = %ld\n", sizeof(C));
C c; // c的内存布局为:m_b1 | m_b1
c.Derived1::m_b1 = 1;
c.Derived2::m_b1 = 1;
return 0;
}
虚基类表vbtable(virtual base table)
虚基类表指针vbptr(virtual base table pointer)
示例1
|-----------|
| Base |
|-----------|
|
---------------
virtual | | virtual
\|/ \|/
|----------| |----------|
| Derived1 | | Derived2 |
|----------| |----------|
#include
#include
class Base
{
public:
int m_b;
};
class Derived1 : virtual public Base
{
public:
int m_d1;
};
class Derived2 : virtual public Base
{
public:
int m_d2;
};
int main(int argc, char **argv)
{
printf("sizeof(Base) = %ld\n", sizeof(Base));
printf("sizeof(Derived1) = %ld\n", sizeof(Derived1));
Derived1 d1;
d1.m_b = 0x1111;
d1.m_d1 = 0x2222;
return 0;
}
gdb查看
gdb main
(gdb) b 31
Breakpoint 1 at 0x1230: file main.cpp, line 32.
(gdb) set print pretty on
(gdb) r
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
sizeof(Base) = 4
sizeof(Derived1) = 16
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:32
32 return 0;
(gdb) p &d1
$1 = (Derived1 *) 0x7fffffffdc10
(gdb) p d1
$2 = {
<Base> = {
m_b = 4369
},
members of Derived1:
_vptr.Derived1 = 0x555555557d58 <VTT for Derived1>,
m_d1 = 8738
}
(gdb) x/2xg 0x7fffffffdc10
0x7fffffffdc10: 0x0000555555557d58 0x0000111100002222
其中:
sizeof(Derived1) = 16, &d=0x7fffffffdc10
0x7fffffffdc10~0x7fffffffdc17内容为0x0000555555557d58即_vptr.Derived1
0x7fffffffdc18~0x7fffffffdc1b内容为00002222即d1.m_d1 = 0x2222;
0x7fffffffdc1c~0x7fffffffdc1f内容为00001111即d1.m_b = 0x1111;
由此可得d1的内存布局为: vptr | m_d1 | m_b(虚基类对象大小)
virtual虚继承之后,Derived1、Derived2会被编译器插入一个虚基类表指针
示例2
|-----------| |-----------|
| Base1 | | Base2 |
|-----------| |-----------|
virtual | | public
|---------------|
|
\|/
|----------|
| Derived1 |
|----------|
#include
#include
class Base1
{
public:
int m_b1;
};
class Base2
{
public:
int m_b2;
};
class Derived1 : virtual public Base1, public Base2
{
public:
int m_d1;
};
int main(int argc, char **argv)
{
printf("sizeof(Base1) = %ld\n", sizeof(Base1));
printf("sizeof(Base2) = %ld\n", sizeof(Base2));
printf("sizeof(Derived1) = %ld\n", sizeof(Derived1));
Derived1 d1;
d1.m_b1 = 0xb1;
d1.m_b2 = 0xb2;
d1.m_d1 = 0x2222;
return 0;
}
gdb查看
gdb main
(gdb) b 33
Breakpoint 1 at 0x1237: file main.cpp, line 33.
(gdb) set print pretty on
(gdb) r
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
sizeof(Base1) = 4
sizeof(Base2) = 4
sizeof(Derived1) = 24
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:33
warning: Source file is more recent than executable.
33 return 0;
(gdb) p d1
$1 = {
<Base1> = {
m_b1 = 177
},
<Base2> = {
m_b2 = 178
},
members of Derived1:
_vptr.Derived1 = 0x555555557d38 <VTT for Derived1>,
m_d1 = 8738
}
(gdb) p d1
$2 = {
<Base1> = {
m_b1 = 177
},
<Base2> = {
m_b2 = 178
},
members of Derived1:
_vptr.Derived1 = 0x555555557d38 <VTT for Derived1>,
m_d1 = 8738
}
(gdb) p &d1
$3 = (Derived1 *) 0x7fffffffdc10
(gdb) x/6xw 0x7fffffffdc10
0x7fffffffdc10: 0x55557d38 0x00005555 0x000000b2 0x00002222
0x7fffffffdc20: 0x000000b1 0x00007fff
其中:
sizeof(Derived1) = 24, &d1=0x7fffffffdc10
0x7fffffffdc10~0x7fffffffdc17内容为0x0000555555557d38即_vptr.Derived1
0x7fffffffdc18~0x7fffffffdc1b内容为0x000000b2即d1.m_b2 = 0x000000b2;
0x7fffffffdc1c~0x7fffffffdc1f内容为0x00002222即d1.m_d1 = 0x2222;
0x7fffffffdc20~0x7fffffffdc23内容为0x000000b1即d1.m_b1 = 0x000000b1;
由此可得d1的内存布局为: vptr | m_b2 | m_d1 | m_b1(虚基类对象大小) | padding4 |
|-----------|
| Base |
|-----------|
|
---------------
virtual | | virtual
\|/ \|/
|----------| |----------|
| Derived1 | | Derived2 |
|----------| |----------|
| |
|---------------|
|
\|/
|----------|
| C |
|----------|
#include
#include
class Base
{
public:
int m_b1;
};
class Derived1 : virtual public Base
{
public:
int m_d1;
};
class Derived2 : virtual public Base
{
public:
int m_d2;
};
class C : public Derived1, public Derived2
{
public:
int m_c;
};
int main(int argc, char **argv)
{
printf("sizeof(Base) = %ld\n", sizeof(Base));
printf("sizeof(Derived1) = %ld\n", sizeof(Derived1));
printf("sizeof(Derived2) = %ld\n", sizeof(Derived2));
printf("sizeof(C) = %ld\n", sizeof(C));
C c;// c的内存布局为:vbptr1(from Derived1) | m_d1 | vbptr2(from Derived2) | m_d2 | m_c | m_b1(虚基类对象)
c.m_b1 = 0xb1;
c.m_d1 = 0xd1;
c.m_d2 = 0xd2;
c.m_c = 0xc;
return 0;
}
gdb查看
gdb main
(gdb) b 41
Breakpoint 1 at 0x1257: file main.cpp, line 41.
(gdb) r
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
sizeof(Base) = 4
sizeof(Derived1) = 16
sizeof(Derived2) = 16
sizeof(C) = 40
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:41
41 return 0;
(gdb) p &c
$1 = (C *) 0x7fffffffdc00
(gdb) set print pretty on
(gdb) p c
$3 = {
<Derived1> = {
<Base> = {
m_b1 = 177
},
members of Derived1:
_vptr.Derived1 = 0x555555557c98 <vtable for C+24>,
m_d1 = 209
},
<Derived2> = {
members of Derived2:
_vptr.Derived2 = 0x555555557cb0 <VTT for C>,
m_d2 = 210
},
members of C:
m_c = 12
}
(gdb) x/10xw 0x7fffffffdc00
0x7fffffffdc00: 0x55557c98 0x00005555 0x000000d1 0x00007fff
0x7fffffffdc10: 0x55557cb0 0x00005555 0x000000d2 0x0000000c
0x7fffffffdc20: 0x000000b1 0x00007fff
其中:
sizeof© = 40, &c=0x7fffffffdc00
0x7fffffffdc00~0x7fffffffdc07内容为0x0000555555557c98即_vptr.Derived1 = 0x555555557c98,由于多继承派生类与第一个基类共用一个vptr,即c的vptr=0x555555557c98
0x7fffffffdc08~0x7fffffffdc0b内容为0x000000d1即c.m_d1 = 0x000000d1;
0x7fffffffdc10~0x7fffffffdc17内容为0x0000555555557cb0即_vptr.Derived2 = 0x555555557cb0;
0x7fffffffdc18~0x7fffffffdc1b内容为0x000000d2即c.m_d2 = 0xd2;
0x7fffffffdc1c~0x7fffffffdc1f内容为0x0000000c即c.m_c = 0xc;
0x7fffffffdc20~0x7fffffffdc23内容为0x000000b1即c.m_b1 = 0xb1;
由此可得c的内存布局为: vptr1 | m_d1 | padding4 | vptr2 | m_d2 | m_c | m_b1(虚基类对象大小) | padding4 |