先看这段代码
//代码来自陈皓的博客http://blog.youkuaiyun.com/haoel/article/details/3081328
class Base1 {
public:
int ibase1;
Base1():ibase1(10) {}
virtual void f() { cout << "Base1::f()" << endl; }
virtual void g() { cout << "Base1::g()" << endl; }
virtual void h() { cout << "Base1::h()" << endl; }
};
class Base2 {
public:
int ibase2;
Base2():ibase2(20) {}
virtual void f() { cout << "Base2::f()" << endl; }
virtual void g() { cout << "Base2::g()" << endl; }
virtual void h() { cout << "Base2::h()" << endl; }
};
class Base3 {
public:
int ibase3;
Base3():ibase3(30) {}
virtual void f() { cout << "Base3::f()" << endl; }
virtual void g() { cout << "Base3::g()" << endl; }
virtual void h() { cout << "Base3::h()" << endl; }
};
class Derive : public Base1, public Base2, public Base3 {
public:
int iderive;
Derive():iderive(100) {}
virtual void f() { cout << "Derive::f()" << endl; }
virtual void g1() { cout << "Derive::g1()" << endl; }
};
如果执行代码
Derive d;
Base1* b1_ptr = &d;
Base2* b2_ptr = &d;
Base3* b3_ptr = &d;
cout << b1_ptr << endl << b2_ptr << endl << b3_ptr << endl;
会得到结果
0012FF48
0012FF50
0012FF58
也就是说,虽然三个指针指向同一个对象,但是它们的值确实不同的!!!
这是因为,Base2的指针,不管指向Base2的对象还是子类Derive的对象,任何对这个指针的操作,都是按照Base2类的定义的内存布局来处理的。很明显,在一个Derive对象中Base2的内存布局在保存在Base1内存布局之后,并且有8字节的偏移(虚函数表和变量ibase1)。所以,编译做了一个隐式的转换(我恨这些编译器偷偷干的事),将各个指针指向它自己实际的内存布局。
为了符合这个要求,编译器在背后做了许多事情。例如上面这个例子,还有为什么dynamic_cast是必需的。下面解释dynamic_cast的原因:
对转换 Derive* d_ptr = dynamic_cast<Derive*>(b2_ptr) 来说,首先我们想象一下d_ptr的和b2_ptr的值应该是什么,b2_ptr的值已经有了,是0012FF50,而d_ptr的值则应该是0012FF48(因为Base1类的内存布局在Derive类的内存布局的顶端)。很明显,d_ptr和b2_ptr是有偏移量的,至于偏移量有多少,依赖于编译器的实现,只有编译器知道。因此Derive* d_ptr = b2_ptr是个严重的错误(虽然直观的讲b2_ptr确实指向一个Derive类实例)。
同样为了符合这个要求,虚函数的设计十分复杂。
个人认为,虚函数之所以这么设计的原因(这个得看陈皓的博客),就是要满足任意一个指针,不管指向什么,都要按照由它类定义所定义的内存布局来解释内存,从这个角度理解虚函数问题应该有点新意,有空再写