首先:强调一个概念
定义一个函数为虚函数,不代表函数为不被实现的函数。
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。
定义一个函数为纯虚函数,才代表函数没有被实现。
定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
假设我们有下面的类层次:
class A
{
public:
virtual void foo()
{
cout<<"A::foo() is called"<<endl;
}
};
class B:public A
{
public:
void foo()
{
cout<<"B::foo() is called"<<endl;
}
};
int main(void)
{
A *a = new B();
a->foo(); // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!
return 0;
}
这个例子是虚函数的一个典型应用,通过这个例子,也许你就对虚函数有了一些概念。它虚就虚在所谓"推迟联编"或者"动态联编"上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为"虚"函数。
虚函数只能借助于指针或者引用来达到多态的效果。
虚(virtual)函数是为了实现多态,派生类可以覆写(override)基类的虚函数。
1、多态(polymorphism),即基类指针或者引用,可能指向基类对象,也可能指向派生类对象,会根据运行时具体指向的对象,来调用不同的函数。
2、如果没有多态,则什么类型的指针或者引用,就会调用什么类型的对象的函数。
class Animal {
public:
// 编译时静态绑定
void print1() {
printf("print1 parent class.\n");
}
// 虚函数,告诉编译器,运行时动态绑定。
virtual void print2() {
printf("print2 parent class.\n");
}
virtual void print3() {
printf("print3 parent class.\n");
}
};
class Dog: public Animal {
public:
void print1() {
printf("print1 Dog class.\n");
}
void print2() {
printf("print2 Dog class.\n");
}
};
Animal *pAni;
Dog dog;
pAni = &dog;
pAni->print1();
pAni->print2();
pAni->print3();
输出:
print1 parent class.
print2 Dog class.
print3 parent class.
3、每个包含虚函数的类(也叫虚类),都有一个虚函数表(简称虚表),表项是一个虚函数的地址。
4、如果基类是虚类,则继承类也是虚类。
5、虚类的对象最前面都是一个虚表指针,指向类的虚表。