版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
在讲解虚函数之前需要先区分一下以下定义:重载,重写,重定义
- 重载---同一个类中函数名相同,函数的参数列表不相同的两个及两个以上的函数就是函数重载。注意:函数的返回值不能作为函数是否重载的依据。
- 重写---是在子类继承父类的时候,对父类的虚函数进行了覆盖。重写会使程序发生动态联编,产生多态。
- 重定义---是在子类继承父类的时候,对父类的非虚函数进行了覆盖。
面向对象设计语言具有----多态性。
- 静态多态:函数重载,运算符重载
- 动态多态:通过虚函数实现。
- 静态联编:编译过程中就知道要是调用哪个函数,如重载函数就通过参数来判断要调用哪个函数。
- 动态联编:运行时绑定,通过虚函数实现。
- 使用指针或引用访问非虚函数时,编译器根据指针本身的类型决定要调用哪个函数。而不是不是根据指针指向对象的类型。
- 使用指针访问虚函数时,编译器根据指针指向的类型决定要调用哪个函数,与指针本身类型无关。
派生类可以对基类的虚函数的进行重写(重定义、覆盖):
- 与基类的虚函数的参数类型必须一样
- 与基类的虚函数的参数个数必须一样
- 与基类的虚函数的函数返回值类型必须一样(可以替换为子类类型指针)
C++函数数调用默认不使用动态绑定,触发动态绑定必须满足一下条件:
- 只有指定为虚函数的函数才能进行动态绑定
- 必须通过基类的指针或引用对函数进行调用
哪些函数不能为虚函数
- 构造函数:若为构造函数则不能正确调用 父类的构造函数
- 静态成员函数:静态成员函数对每个类来说只有一份代码,所有对象共享这份代码,它不归属于某个具体对象所有。
- 友元函数:根本就不是成员函数,谈何虚函数
- 内敛函数:
- 赋值操作符重载函数:基类与子类的参数类型不同
C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。
虚函数表指针(vptr) 及虚函数表(bptr)
C++对象模型中数据成员,成员函数的位置:
- 所有对象之外:static和非static成员函数, static数据成员则放在全局静态存储区。
- 对象之内: 所有非static数据成员
virtual函数则有以下机制存放:
- 每个类产生一堆指向虚函数的指针,放在表格中,这个表格被称为虚函数表(bptr)
- 每个对象都被添加一个指向虚函数表的指针(虚函数表指针vptr)。
不同继承模式下对象中虚函数表存在形态:详细类容参考博客
- 单继承:派生类中仅有一个虚函数表。这个虚函数表和基类的虚函数表不是一个表(无论派生类有没有重写基类的虚函数),但是如果派生类没有重写基类的虚函数的话,基类和派生类的虚函数表指向的函数地址都是相同的。
- 多继承:派生类中有多个虚函数表,虚函数的排列方式和继承的顺序一致。派生类重写函数将会覆盖所有虚函数表的同名内容,派生类自定义新的虚函数将会在第一个类的虚函数表的后面进行扩充。