虚函数表历险记

来源:互联网 发布:2016淘宝618报名 编辑:程序博客网 时间:2024/05/16 15:14

今天继续看MFC的时候忽然想起来了前些天面试的时候被问到的一个问题:
如果父类有虚函数,子类重写了这个虚函数,但是参数却变了。那么最终调用的将是父类的虚函数还是子类的虚函数呢?
于是顺便试了下。感觉挺好玩~=。= 所以顺便记下来~ =。=

问题一:子类重写父类的虚函数时,如果参数类型变化,在多态性方面会有什么影响?

测试代码:

#include <iostream>class father{public:    father() :i1(11), i2(22) { std::cout << "father born... " << std::endl; };    virtual ~father() {std::cout << "father dead... " << std::endl;};    int i1;    int i2;    virtual void f() { std::cout << "father... f() " << std::endl; }    virtual void g(int i){ std::cout << "father... g(int) " << i<<std::endl; }};class mother{public:    mother() { std::cout << "mother born... " << std::endl; }    virtual ~mother() { std::cout << "mother dead... " << std::endl; }    virtual void f() { std::cout << "mother... f() " << std::endl; }    virtual void g(unsigned i) { std::cout << "father... g(unsigned) " << i<<std::endl; }};class son : public father, public mother{public:    son() { std::cout << "son born... " << std::endl; };    ~son() { std::cout << "son dead... " << std::endl; }    virtual void f() { std::cout << "son... f() " << std::endl; }    virtual void g(double i) { std::cout << "son... g(double) " << i<<std::endl; }};class grandson : public son{public:    grandson() { std::cout << "grandson born... " << std::endl; }    ~grandson() { std::cout << "grandson dead... " << std::endl; }    virtual void f() { std::cout << "grandson... f() " << std::endl; }    virtual void g(float i) { std::cout << "grandson... g(float) " << i<<std::endl; }};int main(){    grandson *pg = new grandson();    son* ps = pg;    father* pf = pg;    std::cout << " ---------------- " << std::endl;    std::cout << "son 的大小为:\t"<<sizeof(*ps) << std::endl;    std::cout << "grandson 的大小为:\t" << sizeof(*pg) << std::endl;    std::cout << " ---------------- " << std::endl;    pg->g(1.5);    ps->g(1.5);    pf->g(1.5);    std::cout << " ---------------- " << std::endl;    pg->f();    ps->f();    pf->f();    std::cout << " ---------------- " << std::endl;    delete ps;}

运行结果:

father born...mother born...son born...grandson born... ----------------son 的大小为:  16grandson 的大小为:     16 ----------------grandson... g(float) 1.5son... g(double) 1.5father... g(int) 1 ----------------grandson... f()grandson... f()grandson... f() ----------------grandson dead...son dead...mother dead...father dead...

从上边的结果可以看出(放一起省篇幅 >_<):

  1. 析构函数使用虚函数后,可以使删除父类(ps)的时候,其本身如果是子类,那么将能正确的调用到子类的析构函数;否则删除ps的时候,就只能调用到son和son以上的父类了;
  2. 多重继承的时候,将会有多个虚函数表指针;
  3. 如果重写的虚函数参数不完全一致,那么(又经过了其他测试后得到的结论):

    • 不同参数的虚函数将不会覆盖父类的虚函数;
    • 相同参数的虚函数将会覆盖父类或者以上的相应虚函数;
    • 在函数调用时,其实并不关心函数参数,即使父类有更匹配的函数,也会强制转换成当前的虚函数然后调用。

所以其原因也猜测如上。打算验证一下不同参数的时候是不是没有覆盖,但是在强转虚函数表的时候遇到了问题:

class grandson //: public son // 这里先改成不继承{public:    grandson() {}    ~grandson() {}    virtual void f() { std::cout << "grandson... f() " << std::endl; }    virtual void g() { std::cout << "grandson... g() " << std::endl; }};int main() {    grandson* g = new grandson();    typedef void(*F)();    F fp = (F)(*((int*)*(int*)g));    fp();    fp = (F)(*((int*)*(int*)g + 1));    fp();    delete g;}

运行结果:

grandson... f()grandson... g()

然而加上继承之后,就死活运行不过了。。。每次都出“test.exe”已经停止运行。。。O(≧口≦)O
查了半天网页也没查出来,发群里问了同事们,一个同事试了下表示他那里完全正常。。。。。。完!全!正!常!
还有一个同事给我推荐了一本英文书(已加入书目),以及其他几个参与讨论的同事,都没有结论。。。。

然而就在宝宝已经准备将其纳入未解之谜的时候,忽然!
想起来son里边有个虚析构函数,那它的虚函数表应该是也有这个虚函数啊!
那么出来的第一位的虚函数如果是这个析构函数的话,那把它强行转成 void(*f)(),运行时岂不是会出问题了!
(⊙o⊙)
而且虚析构函数不同于其他函数,主动调用的话,也许是会出现不同于一般函数强转的错误吧!
(⊙o⊙)
所以我刚刚在干啥啊~

于是把几个虚析构函数都去掉。。。继续接下来的测试:

在重写虚函数时参数不匹配是什么样的体验?

然而出现了两个问题:
1. 不知道为啥!这样写报错了!

    typedef void(*F)(int);    F fp = (F)(*(int*)*(int*)g1);    fp(111);
  1. 不知道为啥!grandson的这么大的size里边,首位其实只有一个虚函数表指针,剩下的都在末尾!(不过这个不影响)
#include <iostream>class father{public:    father() :i1(11), i2(22), i3(33), i4(44){ std::cout << "father born... " << std::endl; };    ~father() {std::cout << "father dead... " << std::endl;};    int i1, i2, i3, i4;    //virtual void f() { std::cout << "father... f() " << std::endl; }    virtual void g(int){ std::cout << "father... g(int) " << std::endl; }};class mother{public:    mother() { std::cout << "mother born... " << std::endl; }    ~mother() { std::cout << "mother dead... " << std::endl; }    //virtual void f() { std::cout << "mother... f() " << std::endl; }    virtual void g(int i) { std::cout << "father... g(unsigned) " << std::endl; }};class stepmother{public:    stepmother() {}    ~stepmother() {}    virtual void g(int i) {}};class son : public father, public mother, public stepmother{public:    son() { std::cout << "son born... " << std::endl; };    ~son() { std::cout << "son dead... " << std::endl; }    //virtual void f() { std::cout << "son... f() " << std::endl; }    virtual void g(int i) { std::cout << "son... g(long) " << i<<std::endl; }};class grandson : public son // 这里先改成不继承{public:    grandson() { std::cout << "grandson born... " << std::endl; }    ~grandson() { std::cout << "grandson dead... " << std::endl; }    //virtual void f() { std::cout << "grandson... f() " << std::endl; }    virtual void g(int i) { std::cout << "grandson... g(float) " << i<<std::endl; }};int main() {    grandson* g1 = new grandson();    grandson* g2 = new grandson();    son* s = g1;    father* f = g1;    std::cout << "-----------------------" << std::endl;    std::cout << *(int*)g1 << std::endl;    std::cout << *(int*)g2 << std::endl;    std::cout << "-----------------------" << std::endl;    std::cout << sizeof(*g1) << std::endl;    int* iter = (int*)g1;    for (int i = 0; i < sizeof(*g1)/sizeof(int); i++) std::cout << *(iter + i) << " ";    std::cout<<std::endl;    //typedef void(*F)(int);    //F fp = (F)(*(int*)*(int*)g1);    //fp(111);    std::cout << "-----------------------" << std::endl;    delete g1;    delete g2;}

输出结果:

1203718812037188-----------------------2812037188 11 22 33 44 12037212 12037240

最后这一行表示的就是这个grandson里边的所有四个字节四个字节的东东都代表什么;
与以往的直观印象不同,这个虚函数表也并不是一个二维数组;
后来又给它增加了其他stepmother(怎么这么大男子主义),这个数列虽然稍显规律,但是宝宝深感:

还是去看书来的比较快!!!

所以今天就这样了!!

改天细看书吧!

TODO:2个问题

0 0