今天复习(学习)了c++中的虚函数,在这里做一下记录。
virtual函数的作用
虚函数的作用是实现动态绑定,比如我们想定义一个函数,用这个函数实现对不同的类调用相同名的成员函数,首先想到的可能是这样写的:
1 | //假设我们已经声明了Base1、Base2、Derived三个类,Base2继承于Base1,Derived继承于Base2,它们中都有手动声明的print成员函数 |
因为我们不知道我们要调用的是哪个类中的print,所以我们只能传递一个指针,来表示传递了一个对象
1 | Base1 a; |
看似没问题,但是这样的输出结果其实是调用了三遍a的print函数,因为在编译过程中,编译器无法根据指针去判断它指向什么类型的对象,所以display函数已经确定了对象是Base1类型的,他就只能调用这个类的print函数,这与我们要求的动态绑定不符。
如果能够推迟这个函数的表达式,让它到程序运行的时候才决定到底指针是什么类型的,那么是不是就达到了我们的要求呢?那我们又应该怎么做呢?答案就是使用virtual关键字。
virtual函数的使用
我们只需要在声明成员函数的时候,在成员函数的前面加上virtual关键字,就相当于指示了编译器不要在编译阶段决定这个函数,不要做静态绑定,而是要在程序运行的时候实现动态的绑定,比如上面这个例子,我们就可以在每个类的print函数上如此声明:
1 | virtual void print(); |
因为我们要实现的是在程序运行的时候再去决定函数的函数体到底是哪部分,所以最好就不要把虚函数声明成类中的内联函数,这样会导致在编译过程中直接嵌入到代码中。这样只要再执行之前写好的display函数,虽然也是调用的基类指针,但是这样就实现了函数体的动态绑定,根据传入指针的类型不同调用不同的print函数。(虚函数再编译过程中编译器不做决定,而是根据程序运行时的具体情况做决定)
注意,虚函数必须是非静态的成员函数,因为它是动态的,换句话说虚函数是属于具体的对象而不是属于类的,它是需要在程序运行后通过指针指向具体的对象,再判断对象的具体类,再决定调用哪个函数体。
virtual函数使用总结
- 一般成员函数可以是虚函数,构造函数不能是虚函数,析构函数可以是虚函数
- virtual关键字只能在函数原型的声明时添加,而不能在成员函数的实现时
- 在派生类中,可以直接对基类的虚函数进行覆盖
- 虚函数一般不声明为内联函数,因为虚函数是运行时动态调用函数体的,而内联函数在处理时是静态的
- 如果基类声明了virtual关键字的虚函数,在继承时派生类中默认会把这个函数继承成virtual类型(如果继承的虚函数不写virtual关键字,系统会判定这个函数和基类的虚函数原型是否一致,如果一致就会将它也视为虚函数)
- 派生类中的虚函数会隐藏基类中同名函数的所有其他重载形式,如果需要调用基类中虚函数的其他重载形式,还是需要指定类名