问题的历史:
多态性成为面向对象编程的关键特性之一,早在C++发展的早期阶段。其目的是通过基本接口访问对象,而不必关心具体类型。这大大扩展了代码的表达力和灵活性。
问题:
没有多态性,代码就变得不灵活:必须显式地识别对象类型,使用switch/case,手动类型转换。这使得应用程序的维护和扩展变得复杂——添加新类型变得代价高昂或不可行,需更改现有代码。
解决方案:
在C++中,通过使用虚函数实现多态性。类声明虚方法,而子类实现它们。基类提供通用接口,实际操作依赖于指针或引用所指向的对象的实际类型。
代码示例:
#include <iostream> class Animal { public: virtual void speak() const { std::cout << "一些动物的声音\n"; } virtual ~Animal() {} }; class Dog : public Animal { public: void speak() const override { std::cout << "汪!\n"; } }; void makeSound(const Animal& a) { a.speak(); } int main() { Dog dog; makeSound(dog); // 输出: 汪!\n} }
关键特点:
virtual声明。override——这提高了代码的安全性。如果不将基类的析构函数声明为虚的,会发生什么?
通过指向基类的指针删除对象将只调用基类的析构函数,而不会调用派生类的析构函数,这将导致资源泄漏。
代码示例:
class Base { public: ~Base() { /*...*/ } }; class Derived : public Base { public: ~Derived() { /*...*/ } }; Base* obj = new Derived(); delete obj; // 未定义行为: Derived::~Derived不会被调用
可以只声明部分虚拟方法,而保持析构函数为非虚拟吗?
不可以,如果类是多态的(至少有一个虚函数),则析构函数必须是虚拟的,以避免内存或资源泄漏。
虚函数会对声明为static的成员起作用吗?
不,类的静态成员不能是虚拟的,因为它们不属于特定对象,并且不存在动态绑定机制。
override方法,导致错误的重写。非常大的设备类层次结构,每个派生类管理自己的资源(例如,打开的文件),但基类的析构函数不是虚拟的。通过指向基类的指针删除时,资源不会被释放。
优点: 项目构建速度快,虚调用最少。
缺点: 内存泄漏,错误的销毁。维护和扩展极其困难。
经过深思熟虑的多态层次结构,基类具有虚函数和虚析构函数。使用关键字override和RAII原则。
优点: 安全地管理资源,简单的扩展,易于测试。
缺点: 由于vtable查找,性能稍低,如果不必要,过度工程的风险。