虚函数通过动态绑定实现运行时多态,允许基类指针调用派生类重写函数。1. 基类用virtual声明虚函数;2. 派生类以override重写同签名函数;3. 通过基类指针或引用调用时,程序根据实际对象类型确定执行版本。如Shape基类的draw()被Circle和Rectangle重写,Shape*指向派生类时调用对应draw()。4. 虚析构函数确保正确释放资源;5. 构造函数不能为虚;6. 多态有vtable和vptr机制支撑:每个含虚函数的类有vtable存储虚函数地址,每个对象含vptr指向所属类vtable;7. 调用虚函数时通过vptr定位vtable,再按偏移量找到函数地址执行;8. 纯虚函数(=0)定义接口,含纯虚函数的抽象类不可实例化,强制派生类实现其方法,支持开闭原则与设计模式。

在C++中,虚函数是实现运行时多态(Runtime Polymorphism)的关键机制。简单来说,它允许我们通过基类指针或引用调用派生类中重写(override)的成员函数,从而在程序运行时根据对象的实际类型来决定执行哪个函数版本,而非编译时根据指针或引用的类型来决定。
C++中,虚函数实现多态的原理其实挺直观的,但又蕴含着精妙的设计。当你将基类中的某个成员函数声明为
virtual
具体到操作层面,这通常涉及以下几步:
virtual
override
举个例子,假设我们有一个
Shape
Circle
Rectangle
draw()
draw()
Shape
Shape*
Circle
draw()
Circle
draw()
立即学习“C++免费学习笔记(深入)”;
#include <iostream>
#include <vector>
#include <memory> // for std::unique_ptr
// 基类
class Shape {
public:
// 声明为虚函数,允许派生类重写并实现多态
virtual void draw() const {
std::cout << "Drawing a generic shape." << std::endl;
}
// 虚析构函数至关重要,以确保通过基类指针删除派生类对象时,
// 能够正确调用派生类的析构函数,避免资源泄露。
virtual ~Shape() {
std::cout << "Shape destructor called." << std::endl;
}
};
// 派生类 Circle
class Circle : public Shape {
public:
// 使用 override 明确指出重写基类的虚函数
void draw() const override {
std::cout << "Drawing a circle." << std::endl;
}
~Circle() override {
std::cout << "Circle destructor called." << std::endl;
}
};
// 派生类 Rectangle
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a rectangle." << std::endl;
}
~Rectangle() override {
std::cout << "Rectangle destructor called." << std::endl;
}
};
// 客户端代码示例
int main() {
// 创建一个 Shape 指针的 vector
std::vector<std::unique_ptr<Shape>> shapes;
// 添加不同类型的派生类对象
shapes.push_back(std::make_unique<Circle>());
shapes.push_back(std::make_unique<Rectangle>());
shapes.push_back(std::make_unique<Shape>()); // 也可以是基类对象
// 遍历并调用 draw() 函数
// 尽管我们通过 Shape* 调用,但实际执行的是各自派生类的 draw()
std::cout << "--- Drawing shapes ---" << std::endl;
for (const auto& shape_ptr : shapes) {
shape_ptr->draw();
}
std::cout << "--- Shapes drawn ---" << std::endl;
// 当 unique_ptr 超出作用域时,会自动调用析构函数
// 由于 Shape 的析构函数是虚的,会正确调用派生类的析构函数
return 0;
}这段代码清晰地展示了,即使
shapes
std::unique_ptr<Shape>
draw()
Circle
Rectangle
Shape
draw()
虚函数不仅仅是语法糖,它是面向对象设计中实现“开闭原则”(对扩展开放,对修改关闭)的基石。在许多设计模式中,虚函数扮演着核心角色,比如工厂方法模式、策略模式、模板方法模式等。它允许我们定义一套通用的接口,而具体的实现则留给派生类去完成,使得系统易于扩展,而无需修改现有代码。
然而,在使用虚函数时,也有一些常见的误区和需要注意的点:
delete
final
final
override
override
要真正理解虚函数的工作原理,就不得不提其幕后的“英雄”——虚函数表(vtable)和虚指针(vptr)。这虽然是编译器实现细节,但了解它有助于我们更好地使用和理解多态。
当一个类中包含至少一个虚函数时,编译器会为这个类生成一个虚函数表(Virtual Table,简称vtable)。这个vtable本质上是一个函数指针数组,里面存储着该类及其所有基类中所有虚函数的实际地址。每个虚函数在vtable中都有一个固定的偏移量。
php配置文件php.ini的中文注释版是一本由多位作者编著的有关PHP内部实现的开源书籍。从环境准备到代码实现,从实现过程到细节延展,从变量、函数、对象到内存、Zend虚拟机…… 如此种种,道尽PHP之风流。
376
同时,该类的每个对象都会在内存中额外包含一个隐藏的虚指针(Virtual Pointer,简称vptr)。这个vptr通常是对象内存布局的第一个成员(或靠近开头),它指向该对象所属类的vtable。
当通过基类指针或引用调用一个虚函数时,C++的运行时系统会执行以下步骤:
这个过程就是所谓的动态绑定或运行时绑定。它使得程序在运行时才确定要调用的具体函数版本。例如,
Shape* s = new Circle(); s->draw();
s
Circle
s
Circle
Circle::draw()
虚函数机制的进一步发展是纯虚函数(Pure Virtual Function)和抽象类(Abstract Class)。当一个虚函数被声明为纯虚函数时,它就没有了具体的实现,只提供了一个接口声明。
virtual void draw() const = 0;
这里的
= 0
包含纯虚函数的类被称为抽象类。抽象类不能被直接实例化(即不能创建抽象类的对象),它存在的目的就是作为基类,为派生类提供一个统一的接口规范。派生类必须重写(实现)基类中所有的纯虚函数,否则它自身也将成为一个抽象类。
抽象类和纯虚函数在设计中非常有用,它们强制派生类实现某些特定的行为,确保了接口的完整性。这在构建框架或库时尤为关键,它定义了“契约”,要求所有遵循此契约的派生类都必须提供某些功能。
比如,一个
PaymentProcessor
processPayment()
CreditCardPaymentProcessor
PayPalPaymentProcessor
processPayment()
PaymentProcessor
以上就是C++如何实现虚函数实现多态的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号