最近在找实习,靠着大一学的一点C++,然后被怼了n次C++虚函数原理,这个课上也没讲,我们的教材也没有。。。。总结一下。
虚函数实现
虚函数是C++多态的核心。实现多态的原理就是虚函数表。
每个含有虚函数的类都包含了一个虚函数表,表里面的每个表项是指向虚函数的函数指针。在类实例化的时候,会给每个对象分配一个隐藏成员vptr 是指向虚表的指针。比如基类中含有虚函数的话,那么基类的对象会包含一个虚指针vptr指向基类中包含所有虚函数的地址表。派生类对象也会包含一个指向派生类虚函数表的指针vptr。如果派生类中提供了虚函数的新定义,那么在派生类的虚函数表中将存放新函数的地址。如果没有,该虚函数表会保存原来基类的虚函数地址。派生类中定义的新的虚函数也会保存在该虚函数表中。
在用基类指针指向派生类对象的时候,是通过对象的vptr虚指针来进行虚函数的调用的。虽然这里是基类的指针,但是对象的虚表指针是属于基类的,也就是基类可以访问。所以可以实现基类指针调用派生类的方法。这也就是动态联编。程序根据对象的类型调用方法,但是对象的类型只有在运行的时候才能确定,所以被称为动态联编。普通的函数使用静态联编,也就是在编译的过程中确定函数的代码。指针类型在编译时已知,因此在编译时就能将基类的普通函数关联起来。
几个小问题
1、构造函数可以是虚函数吗
不能,因为派生类对象构造的时候将调用派生类的构造函数,再使用基类的,不同于继承的机制。创建对象的时候调用构造函数,不会使用基类指针显式调用,因此声明为虚的没什么意义。
还有一点 构造函数是在对象生命的时候进行调用,在对象还没实例化的时候,就不存在相应的内存空间,也没有对应的虚指针,无法进行虚函数调用。
2、析构函数为什么应该被设为虚函数
在用基类指针指向一个派生类对象的时候,我们释放基类指针,如果是一般的函数,则只会调用基类的析构函数,这样就无法释放派生类对象新的成员指向的内存。因此析构函数应当设置为虚函数,除非类不用做基类。
参考:《C++ Primer Plus》第6版 中文版
博客:CPlusPlusThings https://light-city.club/sc/basic_content/vptr_vtable/
https://leehao.me/C-%E8%99%9A%E5%87%BD%E6%95%B0%E8%A1%A8%E5%89%96%E6%9E%90/ß