虚函数(继承,多态中的理解,vptr和vtbl)

  • Post author:
  • Post category:其他



继承和虚函数


对象模型:关于vptr和vtbl


侯捷大神的课程(讲的巨TM好!有一定难度,但是很精彩,c++学习的小伙伴一定要看!)

我们知道,如果子类继承了父类,那么子类是能够继承父类的所有数据的(如果继承限制允许)

继承中使用虚函数:

不是虚函数:这样的函数在父类中已经设计好,而且是不想要子类继承的

不纯虚函数:这样的函数父类已经实现了,子类在继承父类此函数的时候,能够根据自己的需求使用或者完善这个功能

纯虚函数:所有的功能父类已经实现了,让子类继承这个功能(也是能修改的)

虚函数最经典的用法:

很巧妙的框架:

因为有些函数子类没法实现(或者父类所设计的功能是拿来卖钱,卖给子类),所以就继承父类,通过子类的对象来调用父类的函数 myDoc.OnFileOpen();

让子类定义所继承的父类,完成功能,也就是父类提出了功能,将其延缓了(几年后再用等),子类在用的时候在去定义它,这个方法就是(Template Method)

下面举一个栗子:

某厂面试题:

很经典的

pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i )




https://zhidao.baidu.com/question/372005299.html





poorcowboy


回答:

typedef void( *Fun )( void ); //把Fun定义为一个没有参数,返回void类型的du函数指针

*( ( int* ) * ( int* )( &b ) + i )这一段,

(int*)*相当于没有进行zhi任何操作,所以等同于

*( ( int* )( &b ) + i )

这里先取b的地址dao,然后把地址转换成int*,之后+i是指针算术,也就是在b的地址上加一个int的长度,最后最前面的*是解指针,座椅这段最后返回的是“b的地址+i个int长度的值”

最前面的(Fun)是强制把“b的地址+i个int长度的值”转换为一个“没有参数,返回void类型的函数指针”,所以pFun就是一个函数指针,其指向的位置从一开始的b的地址,每次循环加一个int的长度

然后我们开看,b的地址,b是一个B类型,B类型的第一个函数是g(),而第一次循环pFun的地址就是b的地址,b又没有属性(私有或共有变量)所以b的地址就是b中第一个函数g的地址,所以第一次循环的pFun()相当于调用B::g

( Fun )*( ( int* ) * ( int* )( &b ) + i ); 这里*( ( int* ) * ( int* )( &b ) + i )最前面的*是对上面的结果进行解指针,也就取把b的地址+i个int长度的这个地址的值,并把它转换为Fun类型,也就是一个没有参数,返回void类型的函数指针,所以最后得到的就是一个函数指针。这个指针所指向的地址就是

然后我们来看循环,循环中3次pFun变量分别被赋了3次值,每次都是一个函数指针

由于B类型中有virtual函数,所以b的地址指向的是b的vtbl(如果你不知道这个,你面试就没戏了),vtbl可以看作一个保存了函数指针的数组,每个元素就是一个int长度,在vtbl中B::g,A::f,B::h是按照如上顺序排列的,所以第一次循环指向B::g,那么后两次就指向A::f和B::h了

至于为什么是按照这样的顺序排列的,是因为其声明顺序,首先是父类的virtual函数按照其生命顺序放入vtbl中,然后是子类的放进去,所以其顺序是:A::h,A::f(这是父类的声明顺序)子类中只有B::H是新声明的,所以顺序是A::g,A::f,B::h。

又因为b的类型是b,你知道什么事多态和动态绑定,就明白第一个调用的为什么是B::g而不是A::g了。


关于C++ 对象的内存布局


haoel


大神的文章:


https://blog.csdn.net/haoel/article/details/3081328


上边说的就是虚函数中的vptr和vtbl,以及继承和多态的使用


对象模型:关于vptr 和vtbl

通过指针p 调用C::vfunc1()

编译器看到一个调用的动作,在过去c的时代,是把它编译成一个特定的语法,call调用***一个地址,你要调用哪一个函数,编译器就跳到一个地方去,将来在return 回来,这就是静态绑定,精准到地址。

现在通过指针调用虚函数的时候,是面向对象设计的关键,避免繁琐的类型判断动态绑定(虚机制),逻辑意义就是走的vptr->vtbl->fun()


(


条件:

通过指针调用,

指针p需要向上转型,子类->父类,即new的对象是子类,声明时是父类,保证安全

必须要有虚函数)


虚函数的指一种用法就是多态,指针指向不同的类型list<A*>mylist

解析成c:  (*(p->vptr)[n])(p);

(*p->vptr[n])(p);



版权声明:本文为m0_38089031原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。