虚函数是面向对象编程函数的一种特定形态,是C++用于实现多态的一种有效机制。
1、什么是虚函数?
指向基类的指针在操作它的多态类对象时,会根据不同的类对象调用其相应的函数,这个函数就是虚函数,虚函数用virtual修饰函数名。虚函数的作用是在程序的运行阶段动态地选择合适的成员函数。在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型,(参数类型的顺序也要一致),以实现统一的接口。如果在派生类中没有重新定义虚函数,则它继承基类的虚函数。
使用虚函数时需要注意一下几个方面:
(1)只需在声明函数的类体中使用关键字virtual将函数声明为虚函数,在定义函数时不需要
(2)将基类中某一成员函数声明为虚函数后,派生类中的同名函数自动成为虚函数
(3)如果类(基类和派生类)中声明了某成员函数为虚函数,则类中不能再出现与之相同的非虚函数
(4)非类的成员函数不能定义为虚函数,全局函数以及类的静态成员函数(因为调用类的静态成员函数不需要实例,但调用虚函数需要一个实例)和构造函数(因为构造函数是在对象完全构造之前运行的,构造函数是初始化虚表指针,而当要调用虚函数时需要知道虚表指针,存在矛盾,但构造函数里是可以调用虚函数的)、内联函数也不能定义为虚函数,但一般将析构函数定义为虚函数(如果不把析构函数定义为虚函数,当用基类指针delete时,无法调用派生类的析构函数,派生类部分无法析构)
(5)指针声明不调用构造函数
编译器发现一个类中有虚函数时,会立即为此类生成一个虚函数表,虚函数表的各表项为指向对应虚函数的指针。编译器还会在此类中隐含插入一个指针vptr指向虚函数表。另外在调用类的构造函数时,指向基础类的指针此时已经变成指向具体类的this指针,这样依靠this就可以得到正确的table,这样才能真正与函数体进行连接,这就是动态联编,实现多态的基本原理。
2、C++如何实现多态?
C++中通过虚函数实现动多态。虚函数的本质就是通过基类访问派生类定义的函数。每一个含有虚函数的类,其实例对象内部都有一个虚函数表指针,该虚函数表指针被初始化为本类的虚函数表的内存地址。所以在程序中,不管对象类型如何转换,但该对象内部的虚函数表指针是固定的,这样才能实现动态地对对象函数进行调用,这就是C++多态性的原理。
假设B由A继承而来,A *a=new B();
基类指针a指向派生类对象,加入定义了虚函数,那么有a调用虚函数会调用B中重新定义的虚函数。
3、C++中继承、虚函数、纯虚函数分别指什么?
继承:在C++中继承的使用方式如下:class 派生类名:public/protected/private 基类名{}
。是指一种事物自动获得另一种事物的全部东西(属性,能力)。
虚函数:用virtual修饰的函数就是虚函数,它实现了类的多态。以基类对象的身份调用虚函数,如果对象是派生类,派生类的对应函数就会被调用,从而可以实现通过完全相同的调用形式让不同的类型的对象做出自己不同的响应。
纯虚函数:是一种特殊的虚函数
class 类名
{
virtual 函数返回值类型 虚函数名(形参表)=0;
···
};
对于纯虚函数,编译器要求在派生类中予以重载以实现多态性,含有纯虚函数的类成为抽象类,抽象类不能生成对象。纯虚函数永远不会被调用,他们只要用来统一管理子类对象。
4、是否可以把每个函数都声明为虚函数呢?
虽然虚函数很有效,但是不可以把每个函数都声明成虚函数。因为使用虚函数要付出内存代价,每个虚函数的对象在内存中都必须维护一个虚函数表,额外产生了系统开销。
引申:
(1)C++中如何阻止一个类被实例化?
通过使用抽象类或将构造函数声明为private阻止一个类被实例化。
(2)一般在什么时候将构造函数声明为private?
例如,要阻止编译器生成默认的复制构造函数的时候。
(3)什么时候编译器会生成默认的复制构造函数?
只要自己没写而程序需要,都会生成。
(4)如果已经写了一个构造函数,编译器还会生成复制构造函数吗?
会。