虚函数表历险记

今天继续看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 的大小为:  16
grandson 的大小为:     16
 ----------------
grandson... g(float) 1.5
son... g(double) 1.5
father... 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;
}

输出结果:

12037188
12037188
-----------------------
28
12037188 11 22 33 44 12037212 12037240

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

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

所以今天就这样了!!

改天细看书吧!

TODO:2个问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值