1.虚函数
参考文章:https://www.jianshu.com/p/d07e0ac0ba3c?from=singlemessage
在基类中,虚函数被virtual关键字修饰,并在派生类中重新被定义。
作用:允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类的同名函数。
虚函数与Java中的覆盖同属于运行(动态)多态。
与Java中的覆盖类似,在派生类重新定义时,函数原型(函数类型、函数名、参数个数和类型顺序),都必须与基类中的原型完全相同。
当一个成员函数被定义为虚函数之后,其派生类中符合虚函数特点的同名函数会自动成为虚函数,所以在派生类中重新定义虚函数时,virtual可以写或不写,但为了阅读方便,最好每一层都加上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include<iostream> using namespace std;
class Father { public: virtual void display() { cout<<"Father::display()\n"; }
void fatherShowDisplay() { display(); } };
class Son:public Father { public: void display() { cout<<"Son::display()\n"; } };
int main() { Son son; son.fatherShowDisplay(); }
|
与Java类似,如果父类中有m1,m2两个方法。子类覆盖了m2方法。那么如果调用m1,则m1中调用的m2会是子类中定义的m2。
虚函数必须是成员函数,且不能是友元函数或静态函数,因为它的调用依赖特定的对象来决定。
通过对象名和点运算调用是静态联编,只有通过基类指针的方式才是运行多态。

注意:上图不是虚函数,下图才是,两个对比来看。

对比上面两张图,一个定义虚函数,一个没有,很容易就理解虚函数的作用。
下图也是,左边定义了虚函数,右边没有定义。



2.虚析构函数

注意上图第二个例子,delete时只调用B的析构函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <cstdio> using namespace std; class B{ public: ~B(){ cout << "call ~B()\n"; } }; class D:public B{ public: ~D(){ cout << "call ~D()\n"; } }; int main(){ D obj;
return 0; }
|
先调用派生类的析构函数,再调用基类的析构函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> #include <cstdio> using namespace std; class B{ public: ~B(){ cout << "call ~B()\n"; } }; class D:public B{ public: ~D(){ cout << "call ~D()\n"; } }; int main(){ B* p; p = new D; delete p; return 0; }
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <cstdio> using namespace std; class B{ public: virtual ~B(){ cout << "call ~B()\n"; } }; class D:public B{ public: ~D(){ cout << "call ~D()\n"; } }; int main(){ B* p; p = new D; delete p;
return 0; }
|

3.虚函数与重载函数的关系


4.虚函数与多重继承

