c++虚函数机制

来源:互联网 发布:骚气的诗词 知乎 编辑:程序博客网 时间:2024/05/01 01:27

今天看到一道c++笔试题,描述如下:

    #include "stdio.h" class A{public:    virtual void Test()    {        printf("A test\n");    }};class B: public A{public:    void func()    {        Test();    }    virtual void Test()    {        printf("B test\n");    }};class C: public B{public:    virtual void Test()    {        printf("C test\n");    }};void main(){    C c;    ((B *)(&c))->func();    ((B)c).func();}
输出是什么?

正确的答案是:

C test
B test
为什么呢,明明func函数并不是虚函数
((B *)(&c))->func();为什么虚函数机制会起作用呢?
我认为这个要从c++虚函数实现的原理来讨论。在《深度探索c++对象模型》里描述了现在c++虚函数机制实现原理是通过虚函数表来实现的。

也就是说对象c在内存中的存储是这样的int loc-指向虚函数表的指针 _vptr

((B *)(&c))->func();这里可以理解成 B * ptmp = &c; ptmp -> func();
当基类指针指向子类对象,会将子类对象的引用隐式转换成基类型的指针,但是它的本质还是一个子类型的引用。当ptmp调用func()时,由于func()不是虚函数,所以虚函数机制不起作用,c++根据当前的类型去寻找对应的函数,所以会去调用B类的func()函数,而B类的func()函数调用了Test()函数,这个函数是一个虚函数,那么c++就会根据_vptr去寻找对应的虚函数来执行,由于c++是根据内存偏移量来寻找_vptr的,所以此时的_vptr指向的地址表示的是C类的Test函数地址,所以会输出C test;

((B)c).func();当执行这句时,由于把c转换成了基类型的对象,那么他的本质就变成了基类的对象,当执行到Test()时,c++依旧根据偏移量来寻找_vptr,此时找到的_vptr则是父类的_vptr,所以会输出:B test;
个人感觉这里面的坑还是挺大的,只要记住:虚函数机制只有基类通过引用或指针指向派生类时,虚函数机制才会发生;当派生类转换成基类时,派生类的本质就发生了改变,表示一个基类对象,虚函数机制不会发生,c++通过查看变量的类型来决定执行基类的方法。基类的引用也好指针也好,他们在内存中都只是占用8字节空间(64位)而已,声明为基类的引用或指针表示的是指向的内存空间的读取方式应该按照基类的读取方式来读取,但是这个子类的对象本身占用的内存空间是不变的,拥有的虚函数表的指针的偏移量也是固定的,所以虚函数机制可以发生。

1 0
原创粉丝点击