C++_多态和虚构
在C++的类继承中,多态(Polymorphism) 和 虚析构函数(Virtual Destructor) 是两个紧密相关且非常重要的概念,它们共同确保了面向对象编程中动态行为的正确性和资源的安全释放。
一、多态(Polymorphism)
多态是C++面向对象的三大特性之一(封装、继承、多态),它允许同一个接口调用不同的实现。在C++中,多态主要通过虚函数(virtual functions) 实现。
-
虚函数(Virtual Function)
在基类中使用 virtual 关键字声明的成员函数。
派生类可以重写(override)该函数,提供自己的实现。
当通过基类指针或引用调用虚函数时,会根据实际对象的类型来决定调用哪个版本的函数,这就是动态绑定(Dynamic Binding)。
#include <iostream>
using namespace std;class Animal {
public:virtual void makeSound() {cout << "Animal makes a sound" << endl;}
};class Dog : public Animal {
public:void makeSound() override { // override 关键字可选,但推荐使用cout << "Woof!" << endl;}
};class Cat : public Animal {
public:void makeSound() override {cout << "Meow!" << endl;}
};int main() {Animal* animal1 = new Dog();Animal* animal2 = new Cat();animal1->makeSound(); // 输出: Woof!animal2->makeSound(); // 输出: Meow!delete animal1;delete animal2;return 0;
}
注意:如果没有 virtual,则调用的是基类的 makeSound(),这就是静态绑定。
二、虚析构函数(Virtual Destructor)
- 为什么需要虚析构函数?
当你通过基类指针删除一个派生类对象时,如果基类的析构函数不是虚的,那么只有基类的析构函数会被调用,派生类的析构函数不会被调用,这会导致资源泄漏(如内存、文件句柄等未正确释放)。
class Base {
public:~Base() {cout << "Base destructor" << endl;}
};class Derived : public Base {int* data;
public:Derived() { data = new int(10); }~Derived() {delete data;cout << "Derived destructor" << endl;}
};int main() {Base* ptr = new Derived();delete ptr; // 只调用 Base 的析构函数!// Derived 的析构函数不会被调用!// data 指针泄漏!return 0;
}
- 解决方案:将基类的析构函数声明为 virtual
class Base {
public:virtual ~Base() { // 虚析构函数cout << "Base destructor" << endl;}
};class Derived : public Base {int* data;
public:Derived() { data = new int(10); }virtual ~Derived() { // 派生类析构函数自动成为虚函数(可加 virtual,也可不加)delete data;cout << "Derived destructor" << endl;}
};int main() {Base* ptr = new Derived();delete ptr; // 先调用 Derived 的析构函数,再调用 Base 的析构函数return 0;
}
输出:深色版本Derived destructor
Base destructor
-
最佳实践
只要一个类被设计为基类(即可能被继承),其析构函数就应该声明为 virtual。
即使析构函数是空的,也应声明为 virtual。
虚析构函数会带来轻微的性能开销(虚函数表查找),但在大多数情况下这是值得的。
三、总结
概念 作用 关键点
多态 实现运行时动态调用 使用 virtual 函数,通过基类指针/引用调用
虚析构函数 确保派生类对象被正确销毁 基类析构函数必须是 virtual,否则可能导致资源泄漏
✅ 记住:“有虚函数,就有虚析构函数”。如果一个类有虚函数,通常意味着它会被继承和多态使用,因此析构函数必须是虚的。
补充:纯虚函数与抽象类
class Shape {
public:virtual void draw() = 0; // 纯虚函数virtual ~Shape() = default; // 虚析构函数,推荐写成 default
};class Circle : public Shape {
public:void draw() override {cout << "Drawing a circle" << endl;}~Circle() override = default;
};
包含纯虚函数的类是抽象类,不能实例化。
子类必须实现所有纯虚函数,否则也是抽象类。
抽象基类必须有虚析构函数。