虚函数表历险记
来源:互联网 发布: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...
从上边的结果可以看出(放一起省篇幅 >_<):
- 析构函数使用虚函数后,可以使删除父类(ps)的时候,其本身如果是子类,那么将能正确的调用到子类的析构函数;否则删除ps的时候,就只能调用到son和son以上的父类了;
- 多重继承的时候,将会有多个虚函数表指针;
如果重写的虚函数参数不完全一致,那么(又经过了其他测试后得到的结论):
- 不同参数的虚函数将不会覆盖父类的虚函数;
- 相同参数的虚函数将会覆盖父类或者以上的相应虚函数;
- 在函数调用时,其实并不关心函数参数,即使父类有更匹配的函数,也会强制转换成当前的虚函数然后调用。
所以其原因也猜测如上。打算验证一下不同参数的时候是不是没有覆盖,但是在强转虚函数表的时候遇到了问题:
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);
- 不知道为啥!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个问题
- 虚函数表历险记
- 爱情历险记
- 加班历险记
- 买房历险记
- MyBookWorld历险记
- Git历险记
- Git历险记
- git 历险记
- Wifi历险记
- Ubuntu历险记
- Git 历险记
- Jenkins 历险记
- HXH历险记
- Angular历险记
- Linux安装历险记
- 安装opldap历险记
- 我的 Linux 历险记
- ubuntu升级历险记
- 【设计模式之装饰模式】
- 五、排序算法(选择排序、插入排序、希尔排序)
- Project Euler Problem 91-100
- 使用jstack 发现死锁
- Java提高篇----抽象类与接口
- 虚函数表历险记
- LA 4287(p322)----Proving Equivalences
- hdu 4777 树状数组+合数分解
- SQL Server 2008还原数据库时出现“备份集中的数据库备份与现有的数据库不同”的解决方法
- 哈希表
- android中使用codec总是报NoSuchMethodError的问题根源
- 简单选择排序
- 2015年12月27日
- DOM(文档对象模型节点)