当一个基类指针指向一个派生类对象时,通过此指针只能访问基类的成员函数。将成员函数声明为虚函数可以使得程序在运行时能够依据基类指针指向对象的确切类型自动调用相应的函数。
声明虚函数:
virtual<返回类型><函数名><(参数列表)>
如果某个类的一个成员函数为虚函数,就以为着此函数在派生类中可能有不同的实现,在程序运行时通过基类的指针/引用调用此成员函数,会自动根据指针/引用所指向对象的确切类型调用成员函数。
虚函数-多态:
使用虚函数可以获得多态表现,当使用一个基类指针引用去调用成员函数时,其实际调用的成员函数可能不同,从而导致不同的结果。
虚函数-动态联编:
联编是指将函数调用和实际的被调用的函数绑定在一起的过程。
静态联编:又称先期联编,早绑定。当联编出现在编译时候,就称为静态联编。(所有的普通函数和非虚的成员函数都使用静态联编)
动态联编:又称迟后联编,迟绑定。在程序运行的时候发生的联编就是动态联编。C++中只有虚函数会使用动态联编(由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数)
虚函数-覆盖/改写(override):
派生类中可以改写基类的虚函数,定义与基类虚函数不同的实现,这个过程称为覆盖或改写(override)。
如果某成员函数在基类中声明时使用了virtual关键字,如果在派生类中定义了一个同名成员函数,只要该成员函数的参数列表、返回值和const属性都与基类中同名的虚函数一样,则无论是否为该成员函数使用virtual关键字,此函数都将被视为虚函数,并且视为基类虚函数的覆盖。
看下面的例子:
class Base
{
public:
virtual void f1();
void f3();
virtual void f4() const;
}
class Derived:public Base
{
public:
//有没有virtual均可,覆盖/改写基类虚函数
void f1();
//从此类开始成为虚函数,隐藏Base::f3
virtual void f3();
//和Base::f4是两个不同的函数
virtual void f4();
//函数重载,不是虚函数
void f4(int) const;
}
void main()
{
Base *p=new Derived();
//Derived::f1
p->f1();
//Base::f3
p->f3();
//Base::f4
p->f4();
}
纯虚函数和抽象类:
C++允许声明一个不能有实例对象的类,这样的类称为抽象类。抽象类的唯一作用就是被继承。
抽象类至少具有一个纯虚函数,不能定义抽象类的对象。与抽象类的概念相对,没有纯虚函数的类称为具体类。
派生类可以override抽象类的纯虚函数,为抽象类的纯虚函数提供一个合适的实现。除非派生类overide其抽象类的所有纯虚函数,否则派生类仍然为抽象类。
class A{ //抽象类
public:
virtual void vf()=0;
};
class B:public A{ //抽象类
public:
virtual void f2()=0;
};
class C:public B{ //抽象类
public:
virtual void f2(){}
};
class D:public C{ //具体类
public:
virtual void vf(){}
};