多态二

带虚函数的多继承

描述:先是Base1、Base2、Base3都拥有虚函数f、g、h,Derived公有继承以上三个类,在Derived中覆盖了虚函数f,还有一个普通的成员函数g1,四个类各有一个double成员。

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
class Base1
{
public:
Base1()
: _iBase1(10)
{ cout << "Base1()" << endl; }
virtual void f()
{
cout << "Base1::f()" << endl;
}

virtual void g()
{
cout << "Base1::g()" << endl;
}

virtual void h()
{
cout << "Base1::h()" << endl;
}

virtual ~Base1() {}
private:
double _iBase1;
};

class Base2
{
public:
virtual void f()
{
cout << "Base2::f()" << endl;
}

virtual void g()
{
cout << "Base2::g()" << endl;
}

virtual void h()
{
cout << "Base2::h()" << endl;
}
private:
double _iBase2;
};

class Base3
{
public:
virtual void f()
{
cout << "Base3::f()" << endl;
}

virtual void g()
{
cout << "Base3::g()" << endl;
}

virtual void h()
{
cout << "Base3::h()" << endl;
}
private:
double _iBase3;
};

class Derived
: public Base1
, public Base2
, public Base3
{
public:
Derived()
: _iDerived(10000)
{ cout << "Derived()" << endl; }

void f()
{
cout << "Derived::f()" << endl;
}

void g1()
{
cout << "Derived::g1()" << endl;
}
private:
double _iDerived;
};

int main(void)
{
cout << sizeof(Derived) << endl;//56

Derived d;
Base1* pBase1 = &d;
Base2* pBase2 = &d;
Base3* pBase3 = &d;

cout << "&Derived = " << &d << endl;
cout << "pBase1 = " << pBase1 << endl;
//pBase1和d地址一样
cout << "pBase2 = " <<pBase2 << endl;
cout <<"pBase =" << pBase3 << endl;
//pBase1 和 pBase2 和 pBase3相差16字节
//Base1 和 Base2 和 Base3的子对象存放顺序是根据继承顺序决定的
return 0;
}

布局规则

1 . 每个基类都有自己的虚函数表(前提是基类定义了虚函数)

2 . 派生类如果有自己的虚函数,会被加入到第一个虚函数表之中 —— 希望尽快访问到虚函数

3 . 内存布局中,其基类的布局按照基类被声明时的顺序进行排列(有虚函数的基类会往上放——希望尽快访问到虚函数

4 .派生类会覆盖基类的虚函数,只有第一个虚函数表中存放的是真实的被覆盖的函数的地址;其它的虚函数表中对应位置存放的并不是真实的对应的虚函数的地址,而是一条跳转指令 —— 指示到哪里去寻找被覆盖的虚函数的地址

带虚函数的多重继承的二义性

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
class A{
public:
virtual void a(){ cout << "A::a()" << endl; }
virtual void b(){ cout << "A::b()" << endl; }
virtual void c(){ cout << "A::c()" << endl; }
};

class B{
public:
virtual void a(){ cout << "B::a()" << endl; }
virtual void b(){ cout << "B::b()" << endl; }
void c(){ cout << "B::c()" << endl; }
void d(){ cout << "B::d()" << endl; }
};

class C
: public A
, public B
{
public:
virtual void a(){ cout << "C::a()" << endl; }
void c(){ cout << "C::c()" << endl; }
void d(){ cout << "C::d()" << endl; }
};
//先不看D类
class D
: public C
{
public:
void c(){ cout << "D::c()" << endl; }
};
void test0(){
C c;
c.a();//C::a()理解为隐藏,没有通过虚函数调用
c.b();//冲突,成员名访问冲突的二义性问题
c.c();//C::c()同样隐藏
c.d();//C::d()更是隐藏

A * pa = &c;
pa->a();//c::a() 动态多态
pa->b();//A::b() 没有覆盖
pa->c();//C::c() 动态多态
pa->d();//error 只能调用基类子对象自己的函数

B *pb = & c;
pb->a();//C::a() 动态多态
pb->b();//B::b()
pb->c();//B::c() 不是虚函数 没有通过虚表
pb->d();//B::d()

C *pc = &c;
pc->a();//C::a() 指针和对象有区别,指针会通过虚表来找到虚函数,因为C类还可能往下派生,编译器无法确定是不是派生类的指针,如果指向派生类对象,派生类可能对这个虚函数进行了覆盖,所以仍然通过虚表访问
pc->b();//冲突 成员名访问冲突二义性
D d;
pc = &d;
pc->c();//D::c() 此时c()这个成员函数是都应该当成虚函数呢?验证 让D类继承C,如果是虚函数,那么D类的c()函数是可以覆盖C的c()的
//验证可得 c()是一个虚函数,也会通过虚函数表
pc->d();//C::c() 隐藏
}

总结:

  • 如果通过对象来调用虚函数,那么不会通过虚表来找虚函数,因为编译器从一开始就确定调用函数的对象是什么类型,直接到程序代码区中找到对应函数的实现;
  • 如果基类指针指向派生类对象,通过基类指针调用虚函数,若派生类中对这个虚函数进行了覆盖(重写-override),那么符合动态多态的触发机制,最终的效果是基类指针调用到了派生类定义的虚函数;如果派生类对这个虚函数没有进行覆盖,也会通过虚表访问,访问到的是基类自己定义的虚函数的入口地址;
  • 如果是派生类指针指向本类对象,调用虚函数时,也会通过虚表去访问虚函数。若本类中对基类的虚函数进行覆盖,那么调用到的就是本类的虚函数实现,如果没有覆盖,那么会调用到基类实现的虚函数。