今天继续看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...
从上边的结果可以看出(放一起省篇幅 >_<):
- 析构函数使用虚函数后,可以使删除父类(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;
}
输出结果:
12037188
12037188
-----------------------
28
12037188 11 22 33 44 12037212 12037240
最后这一行表示的就是这个grandson里边的所有四个字节四个字节的东东都代表什么;
与以往的直观印象不同,这个虚函数表也并不是一个二维数组;
后来又给它增加了其他stepmother(怎么这么大男子主义),这个数列虽然稍显规律,但是宝宝深感:
还是去看书来的比较快!!!
所以今天就这样了!!
改天细看书吧!
TODO:2个问题