C++ 虚函数是实现多态性的一种重要机制。使用虚函数时,有一些重要的注意事项需要考虑:

  1. 虚函数的声明
  • 在基类中用 virtual 关键字声明虚函数。
  • 派生类中重写该虚函数时,应使用 override 关键字,以提高代码的可读性。
  1. 构造函数和析构函数
  • 构造函数不能是虚函数。因为构造函数是在对象创建时调用的,而这时对象的类型还没有完全确定,所以不能实现多态。
  • 析构函数可以是虚函数,并且通常建议将基类的析构函数声明为虚函数。这样可以确保在删除指向派生类的基类指针时,能够正确地调用派生类的析构函数,从而避免内存泄漏。
  • 在构造函数和析构函数中不应调用虚函数。
  1. 虚函数表(vtable)
  • 每个包含虚函数的类都有一个虚函数表,表中存放着虚函数的地址。
  • 当通过基类指针或引用调用虚函数时,实际上是通过虚函数表来找到正确的函数地址进行调用的。
  1. 覆盖与隐藏
  • 如果派生类中的函数与基类中的虚函数具有相同的名称、返回类型以及参数列表,那么派生类的函数将覆盖基类的虚函数。
  • 如果参数列表不同,则派生类的函数将隐藏基类的同名函数,而不是覆盖它。
  1. 纯虚函数和抽象类
  • 纯虚函数是没有函数体的虚函数,它通过在函数声明的末尾添加 =0 指定。
  • 包含纯虚函数的类是抽象类,不能实例化对象。抽象类主要用于定义接口。
  1. 性能考虑
  • 使用虚函数会增加一些额外的开销,因为每次调用虚函数时都需要进行查找虚函数表的操作。
  • 在性能敏感的应用中,需要权衡使用虚函数带来的灵活性和性能开销。
  1. 多重继承和虚函数
  • 在多重继承中,如果多个基类包含同名的虚函数,派生类需要明确地覆盖这些函数,否则将会产生歧义。
  • 可以使用虚继承来解决多重继承中的“菱形继承”问题,确保基类只有一个实例。

更进一步地,可参对下列关键点的详细介绍:

  1. 有虚函数的基类应具有虚析构函数
  2. 重写的虚函数应声明为 override 或 final
  3. 虚函数参数的默认值应与基类中声明的一致
  4. 不建议虚函数的参数有默认值
  5. 派生类不应重新定义与基类相同的非虚函数
  6. 拷贝和移动赋值运算符不应为虚函数
  7. 比较运算符不应为虚函数
  8. final 类中不应声明虚函数
  9. 多态类的对象作为参数时不应采用值传递的方式
  10. 在构造函数中不应调用虚函数
  11. 在析构函数中不应调用虚函数