上下类型转换与晚绑定
class Mammal
{
public:
virtual speak();
move();
};
Class Dog : public Mammal
{
void speak();
void move();
};
int main()
{
Mammal *pDog=new Dog;
pDog->Move(); // 调用Mammal的方法---Mammal::move//向上类型转换
pDog->speak(); //调用Dog的方法---Dog::speak---虚函数
}
虚函数表
转载博客: http://blog.csdn.net/haoel/article/details/1948051/
- 父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数
- C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
Base b;
- 函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样
- 而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。
无继承的虚函数表
- 在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,其虚函数表如下所示:
- 虚函数按照其声明顺序放于表中。
- 父类的虚函数在子类的虚函数前面。’
一般继承(有虚函数覆盖)
- 覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
- 没有被覆盖的函数依旧。
Base *b = new Derive();
b->f(); //调用Derive::f()
- b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。
多重继承(无虚函数覆盖)
- 每个父类都有自己的虚表。
- 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
- 这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
多重继承(有虚函数覆盖)
- 三个父类虚函数表中的f()的位置被替换成了子类的函数指针。
注意
- 不可通过父类型的指针访问子类自己的虚函数
Base1 *b1 = new Derive();
b1->f1(); //编译出错
- 任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)