C++ 多态
本文部分代码通过DeepSeek生成
创建一个基类和一个子类。
基类 Animal
包含一个虚函数 makeSound()
,子类 Dog
继承自 Animal
并重写了 makeSound()
函数。
#include <iostream>
#include <string>
// 基类 Animal
class Animal {
public:
// 构造函数
Animal(const std::string& name) : name(name) {}
// 虚函数,子类可以重写
virtual void makeSound() const {
std::cout << name << " makes a sound." << std::endl;
}
// 虚析构函数,确保正确释放资源
virtual ~Animal() {}
protected:
std::string name;
};
// 子类 Dog,继承自 Animal
class Dog : public Animal {
public:
// 构造函数
Dog(const std::string& name) : Animal(name) {}
// 重写基类的虚函数
void makeSound() const override {
std::cout << name << " says: Woof! Woof!" << std::endl;
}
};
int main() {
// 创建基类对象
Animal* myAnimal = new Animal("Generic Animal");
myAnimal->makeSound(); // 输出: Generic Animal makes a sound.
delete myAnimal;
// 创建子类对象
Animal* myDog = new Dog("Buddy");
myDog->makeSound(); // 输出: Buddy says: Woof! Woof!
delete myDog;
return 0;
}
代码说明:
-
基类
Animal
:-
包含一个成员变量
name
,用于存储动物的名字。 -
包含一个虚函数
makeSound()
,子类可以重写这个函数。 -
包含一个虚析构函数,确保在删除派生类对象时正确调用析构函数。
-
-
子类
Dog
:-
继承自
Animal
,并重写了makeSound()
函数,使其输出狗的声音。 -
使用
override
关键字明确表示重写基类的虚函数。
-
-
main
函数:-
创建了一个
Animal
对象和一个Dog
对象,并调用它们的makeSound()
函数。 -
使用
delete
释放动态分配的内存。
-
在 C++ 中,虚析构函数的作用是确保在通过基类指针删除派生类对象时,能够正确调用派生类的析构函数。如果没有虚析构函数,可能会导致派生类的析构函数不被调用,从而引发资源泄漏或其他未定义行为。
为什么需要虚析构函数?
当使用基类指针指向派生类对象时,如果基类的析构函数不是虚函数,那么当删除该指针时,只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类中分配的资源(如动态内存、文件句柄等)无法正确释放。
示例代码
以下是一个示例,展示了虚析构函数的作用:
#include <iostream>
// 基类
class Base {
public:
Base() {
std::cout << "Base constructor called." << std::endl;
}
// 虚析构函数
virtual ~Base() {
std::cout << "Base destructor called." << std::endl;
}
};
// 派生类
class Derived : public Base {
public:
Derived() {
std::cout << "Derived constructor called." << std::endl;
}
~Derived() {
std::cout << "Derived destructor called." << std::endl;
}
};
int main() {
// 使用基类指针指向派生类对象
Base* ptr = new Derived();
// 删除指针
delete ptr; // 正确调用派生类和基类的析构函数
return 0;
}
输出结果
Base constructor called.
Derived constructor called.
Derived destructor called.
Base destructor called.
关键点
-
虚析构函数的作用:
-
当
delete
一个基类指针时,如果基类的析构函数是虚函数,程序会先调用派生类的析构函数,再调用基类的析构函数。 -
如果基类的析构函数不是虚函数,则只会调用基类的析构函数,而不会调用派生类的析构函数。
-
-
没有虚析构函数的情况:
如果去掉Base
类析构函数的virtual
关键字,输出将变为:
Base constructor called.
Derived constructor called.
Base destructor called.
可以看到,Derived
类的析构函数没有被调用,这可能导致资源泄漏。
-
何时使用虚析构函数:
-
如果一个类可能被继承,并且可能通过基类指针删除派生类对象,那么这个类的析构函数应该是虚函数。
-
如果类不会被继承,或者不会通过基类指针删除派生类对象,则不需要虚析构函数。
-
总结
虚析构函数是 C++ 中多态性的重要组成部分,确保在删除派生类对象时能够正确调用派生类和基类的析构函数。在设计基类时,如果存在继承的可能性,务必为基类声明虚析构函数。