再次翻阅深度探索C++对象模型,算是对虚函数机制又多了一层理解了。以例子说明
- class ZooAnimal
- {
- public:
- ZooAnimal();
- virtual ~ZooAnimal();
- virtual void animate();
- virtual void draw();
- // ....
- private:
- // ZooAnimal的animate()和draw()所需要的数据
- };
- class Bear: public ZooAnimal
- {
- public:
- Bear();
- void animate(); // 虽然没有显式表明是virtual,但是从基类继承virtual属性
- void draw(); // 虽然没有显式表明是virtual,但是从基类继承virtual属性
- virtual void dance();
- // ....
- private:
- // Bear的animate()和draw()以及dance()需要的数据
- };
上面定义了ZooAnimal类和Bear类,我们来解释下在以下两种情况中编译器会做哪些操作:
例1:
Bear yogi;
ZooAnimal franny = yogi ;
例2:Bear yogi;
ZooAnimal *franny = &yogi;
在上面两个例子中,我们要是同时调用franny.draw() 会发生什么情况呢?
在例1中,实际上是调用的ZooAnimal 类版本的draw函数,而在例2中,将调用Bear类版本的ZooAnimal函数。
原因是:
例1中相当于执行的合成的复制构造函数的操作,在复制构造函数中,会显示设定对象franny的vptr指向ZooAnimal class 的virtual table。
例2中相当于进行了指针的强制类型转换,而指针的强制类型转换的本质就是截取地址的多大部分,具体原理请参见:http://blog.sina.com.cn/s/blog_4e967c8b0100zh6h.html
以上可知要触发动态绑定,必须满足两个条件:第一:只有指定为虚函数的成员函数才能进行动态绑定。第二,必须通过基类类型的引用或者指针进行函数调用。举例:
double print_total(const Item_base&, size_t);
Item_base item;
print_total(item, 10); // OK
Bulk_item bulk;
print_total(bulk, 10); // OK 引用bulk中Item_base的部分。
Item_base *item = &bulk; // OK , 指针指向bulk 的Item_base部分。