C++构造函数语义学(二)(基于C++对象模型)

带有虚函数的情况。

下面情况编译器也会在需要的时候为其合成。

1.如果一个类自己声明为虚函数.

 1 #include<iostream>
 2 using namespace std;
 3 class Base
 4 {
 5 public:
 6     virtual void foo(){}
 7 };
 8 int main()
 9 {
10     Base b;
11     while (1);
12     return 0;
13 }

分析:由于类中声明了虚函数,所以编译器会为其合成一个出来,这个合成的默认构造函数的作用是用来保存虚函数表的指针。

 

2.如果一个类继承了带虚函数的类。

1)单继承情况

 1 #include<iostream>
 2 using namespace std;
 3 class Base
 4 {
 5 public:
 6     virtual void foo(){}
 7 };
 8 class Deprive:public Base
 9 {
10 public:
11     void foo() override {
12 
13     }
14 };
15 int main()
16 {
17     Deprive d;
18     while (1);
19     return 0;
20 }

分析:由于继承关系,所以类Base的虚函数属性也被Deprive继承下来,由于类Deprive中没有自己的构造函数,所以此时编译器会为其合成一个出来,同样,在这个合成的默认构造函数中,所做的工作是保存虚函数表的指针。(这里通过sizeof(Deprive)可以看出,类的大小还是4。也就是说,类Base和类Deprive共用了一个虚函数表指针).

 

2)多继承情况

 1 #include<iostream>
 2 using namespace std;
 3 class Base1
 4 {
 5 public:
 6     virtual void foo1(){}
 7     virtual void foo1(){}
 8 };
 9 class Base2
10 {
11 public:
12     virtual void func1(){}
13     virtual void func2(){}
14 };
15 class Deprive:public Base1,public Base2
16 {
17 public:
18     void foo1() override {}
19     void func2()override {}
20     virtual void My_HanShu(){}
21 };
22 int main()
23 {
24     Deprive d;
25     while (1);
26     return 0;
27 }

分析:同样,在多继承体系中,编译器会为类Deprive合成一个默认的构造函数出来,用来保存虚函数表的地址,这里通过sizeof(Deprive)可以看出大小为8字节,所以在默认的构造函数中有两个虚函数表指针,一个是与基类共用的,另一个是另外一个基类的。

注:多继承时,按照继承的顺序,子类的虚函数表指针与第一个继承的父类共用。

 

3.类派生自一个继承链串。

 1 #include<iostream>
 2 using namespace std;
 3 class Base1
 4 {
 5 public:
 6     virtual void foo1(){}
 7 };
 8 class Base2:public Base1
 9 {
10 public:
11 };
12 class Deprive:public Base2
13 {
14 public:
15 };
16 int main()
17 {
18     Deprive d;
19     while (1);
20     return 0;
21 }

分析:这种情况本质没有什么区别,只要基类有虚函数在,那么不管他的派生类的链串有多长,虚函数的属性就会被继承,虚函数属性被继承了,接下来的分析就和前面相同了。

 

下面是C++对象模型中的例子(我添加了一些代码以便运行的效果):(书P45页)

 1 #include<iostream>
 2 using namespace std;
 3 class Widget 
 4 {
 5 public:
 6     virtual void flip()=0;
 7 };
 8 void flip(Widget& widget)
 9 {
10     widget.flip();
11 }
12 class Bell:public Widget
13 {
14 public:
15     void flip()
16     {
17         cout << "Bell" << endl;
18     }
19 };
20 class Whistle:public Widget
21 {
22 public:
23     void flip()
24     {
25         cout << "Whistle" << endl;
26     }
27 };
28 void foo()
29 {
30     Bell b;
31     Whistle w;
32     flip(b);
33     flip(w);
34 }
35 int main()
36 {
37     foo();
38     while (1);
39     return 0;
40 }

 分析:由于编译器在合成的默认构造函数中添加了虚函数表指针,所以接下来在函数void flip(Widget& widget)中的调用是通过虚函数表走的。

widget.flip()的编译器视角就是:*widget.vptr[1](&widget)  (关于布局写法以后再深谈)。

 

posted @ 2020-06-23 10:56  sunshine_gzw  阅读(143)  评论(0编辑  收藏  举报