Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

C++-继承

简介

学习一下C++里面继承相关的知识。

C++-继承

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#pragma pack(push,1)
class A
{
public:
A() { a = 0; }
int a;
virtual void fun() { std::cout << "A::fun()\n"; };
};

class B : public A
{
public:
B() { a = 2; b = 2; }
int b;
virtual void fun() { std::cout << "B::fun()\n"; };
};

class C : public A
{
public:
C() { a = 3; c = 3; }
int c;
virtual void fun() { std::cout << "C::fun()\n"; };
};

class D: public B,public C
{
public:
virtual void fun() { std::cout << "D::fun()\n"; };
};

class B1 : virtual public A
{
public:
B1() { a = 22; b = 22; }
int b;
virtual void fun() { std::cout << "b1::fun()\n"; };
};

class C1 : virtual public A
{
public:
C1() { a = 33; c = 33; }
int c;
virtual void fun() { std::cout << "c1::fun()\n"; };
};

class D1 : public B1, public C1
{
public:
virtual void fun() { std::cout << "D::fun()\n"; };
};
#pragma pack(pop)

int main(int argn, char* argc[]) {
std::cout << "Hello the world!" << std::endl;
A a; // {_vptr$A = 0x4020a0 <vtable for A+16>, a = 0}
// 多态是使用 virtual function table + virtual pointer

B b; // {_vptr$A = 0x4020d0 <vtable for B+16>, a = 2}, b = 2}
C c; // {_vptr$A = 0x402108 <vtable for C+16>, a = 3}, c = 3}
D d; // {<B> = {<A> = {_vptr$A = 0x402140 <vtable for D+16>, a = 2}, b = 2}, <C> = {<A> = {_vptr$A = 0x402158 <vtable for D+40>, a = 3}, c = 3}, <No data fields>}
std::cout << "a " << sizeof(a) << std::endl;//12 = 虚表指针:_vptr$A 8 byte + int(4)
std::cout << "b " << sizeof(b) << std::endl;//16 = 虚表指针:_vptr$A 8 byte + A::a int(4) + B::b int(4)
std::cout << "c " << sizeof(c) << std::endl;//16 = 虚表指针:_vptr$A 8 byte + A::a int(4) + C::c int(4)
std::cout << "d " << sizeof(d) << std::endl;//32 = 虚表指针:_vptr$A 8 byte + B::A::a int(4) + B::b int(4) + _vptr$A 8 byte + C::A::a int(4) + C::c int(4)
// b和c拥有独立的A对象,互相不共享,意味着,如果想在d层级调用A变量需要指定基类。
d.fun();

//std::cout << "d.a" << d.a << std::endl;// 这样会有问题
std::cout << "d.B::a" << d.B::a << std::endl;// 他们是分离的
std::cout << "d.C::a" << d.C::a << std::endl;
std::cout << std::endl;

// virtual inherit 代表共享使用相同的A
B1 b1; // {<A> = {_vptr$A = 0x4021d8 <vtable for B1+56>, a = 22}, _vptr$B1 = 0x4021b8 <vtable for B1+24>, b = 22}
C1 c1; // {<A> = {_vptr$A = 0x402258 <vtable for C1+56>, a = 33}, _vptr$C1 = 0x402238 <vtable for C1+24>, c = 33}
D1 d1; // {<B1> = {<A> = {_vptr$A = 0x4022f8 <vtable for D1+88>, a = 33}, _vptr$B1 = 0x4022b8 <vtable for D1+24>, b = 22}, <C1> = {_vptr$C1 = 0x4022d8 <vtable for D1+56>, c = 33}, <No data fields>}
std::cout << "b1 " << sizeof(b1) << std::endl;// 24 = 虚表指针:_vptr$A 8 byte + a int(4) + _vptr$B1 8 byte + b int(4)
std::cout << "c1 " << sizeof(c1) << std::endl;// 24 = 虚表指针:_vptr$A 8 byte + a int(4) + _vptr$C1 8 byte + c int(4)
std::cout << "d1 " << sizeof(d1) << std::endl;// 36 = 虚表指针:_vptr$A 8 byte + a int(4) + _vptr$B1 8 byte + b int(4) + _vptr$C1 8 byte + c int(4)

std::cout << "d1.B1::a " << d1.B1::a << std::endl; // 这两个值都会相同
std::cout << "d1.C1::a " << d1.C1::a << std::endl;
std::cout << std::endl;

return 0;
}

分析

在C++中,多态是通过虚函数的动态绑定来实现的。vptr(virtual pointer)和vtbl(virtual function table)。vptr指针是和object绑定。vtbl是和class绑定。

使用虚拟继承节约A类重复的数据结构。我们将A中填充一些内存块,将会看到使用非虚拟继承的类,将会膨胀的很厉害。