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) (关于布局写法以后再深谈)。