C++ 中如何从派生类调用虚函数

2025年5月15日 | 阅读 7 分钟

在本文中,我们将讨论如何在 C++ 中从派生类调用虚函数及其优势。

引言

多态性 是面向对象编程(尤其是在 C++ 中)的主要特性之一。换句话说,它指的是多种形式的出现。当派生类为其基类中已定义的函数原型提供实现时,就会出现这些不同的形式。没有这个概念,代码的可重用性和灵活性将受到损害,因为它使程序员能够生成适用于各种项目的通用代码。

问题陈述

为此,在 C++ 中处理多态性时,可能会出现需要从派生类内部调用虚函数的情况。当您需要更改继承方法的某些方面,使其在当前上下文中更相关或更有用时,这种情况经常发生。尽管如此,正确调用重写的虚函数需要承担此类任务的人理解继承和动态分派如何与 C++ 中的虚函数协同工作。

虚函数: 在 C++ 中,虚函数 指的是类中的任何成员函数,可以在其派生版本中重新定义,从而根据用于调用它们的对象的实际类型启用不同的实现。这很重要,因为没有这样的功能,就不可能实现多态性,从而使单个接口能够表示多种底层行为。

  • 虚函数确保无论如何使用指针引用对象,都会为该对象调用正确的函数。
  • 它们的主要用途是实现运行时多态性。
  • 在基类中,函数用 virtual 关键字声明。
  • 函数调用的解析在运行时完成。

解决方案

以下是在 C++ 中从派生类调用虚函数的步骤:

  1. 创建基类: 首先定义一个基类,其中包含我们要在派生类中重写的虚函数。以下是一个示例

请注意派生类中使用的 override 关键字。此关键字告诉编译器该函数旨在重写基类中的虚函数。如果函数签名与基类函数不匹配,它有助于捕获错误。

2. 实例化对象并调用虚函数: 现在,我们可以创建基类和派生类的对象并调用虚函数。以下是如何执行此操作的示例

在此代码中,basePtr 是指向 Base 类对象的指针。最初,它指向一个 Base 对象(baseObj)。通过 basePtr 调用 virtualFunction 时,它会调用指针指向的类中定义的函数版本。稍后,basePtr 会指向一个 Derived 类对象(derivedObj),再次调用 virtualFunction 会调用 Derived 类中重写的版本。

理解动态分派: 在运行时调用正确虚函数的魔法称为动态分派。当我们通过基类指针或引用调用虚函数时,C++ 运行时系统会在运行时确定实际对象类型,并根据对象类型调用相应的函数版本。

程序

让我们举一个例子来说明如何在 C++ 中从派生类调用虚函数。

输出

 
Dog barks
Cat meows

说明

  • 带虚函数的基类: 在此示例中,我们有一个名为 Animal 的基类,它有一个名为 makeSound 的虚函数。它是通过使用 “virtual” 关键字指定的。它可以在派生类中重写。此类本身为 makeSound 方法提供了默认定义
  • 派生类和重写: 该代码还演示了两个派生类:Dog 和 Cat,它们都继承自 Animal 基类。这些类使用它们自己的版本重写了 makeSound 方法;一个会发出狗叫声,另一个会发出猫叫声
  • 动态分派和多态性: 在关键函数中,创建了两个 Cat 和 Dog 类型的对象(分别为 cat 和 dog)。这些对象在不同时间使用类型为 Animal* (animalPtr) 的指针进行指向。它展示了多态性和动态分派。如果 animalPtr 指向 Dog 对象并通过它调用 makeSound,则将执行 Dog 类中已重写的 makeSound 函数(“狗叫”)。同样,如果 animalPtr 指向 Cat 对象,它会回溯到 Cat 类,我们在其中也重写了此方法(“猫叫”)。
  • 优势和用例: 继承结合虚函数可促进可重用性和灵活性,因为它允许我们“编写一次,使用多次”。它提供了一个机会,在基类中定义一个通用接口,该接口随后由 Dog 或 Cat 等派生类通过 makeSound 等方法以不同的方式实现。当需要处理各种对象但它们在某处又具有某些相似性时,这种方法特别有用,因此将它们归为一个大类,以便以后在维护阶段处理这些模块的人可以轻松地使用它们,因为它们更具模块化。

优点

  1. 多态性: 虚函数允许多态性,这意味着它使我们能够编写使用各种派生类对象但通过通用基类接口工作的代码。这个概念通过仅提供一个函数签名,该签名在运行时根据实际对象类型具有多个实现,从而增强了代码的灵活性和可重用性。
  2. 代码组织和模块化: 继承结合虚函数有助于以分层方式组织代码。基类建立共享接口和功能,而派生类提供特定的接口和功能。这使得程序的不同部分可以被隔离和单独管理,从而促进了模块化。
  3. 可扩展性: 通过使用虚函数,我们可以轻松地扩展功能而无需更改任何现有代码。可以通过将新子类添加到此层次结构中来引入增强应用程序功能的新行为或功能,而不会影响已编写的部分。
  4. 动态分派: 根据 C++ 中通过虚函数实现的动态分派功能确定的实际对象类型,在运行时调用相应的函数实现。这种行为对于实现多态行为是必需的,从而在面向对象的设计中实现运行时适应性和灵活性。
  5. 抽象和封装: 继承和虚函数的支持是实现抽象和封装的重要组成部分。在这种情况下,它的意思是,虽然在基类上定义了接口而没有指定确切的实现细节,但数据和行为被打包在类中,从而清晰地区分了不同的关注点。
  6. 减少代码冗余: 虚函数通过允许在一个基类中编写一次通用功能并在派生类中通过继承再次使用它们来减少代码的重复。因此,程序会变短,更容易维护。
  7. 运行时灵活性: 由于虚函数实现了运行时多态性,因为它们使得这种行为成为可能,即关于调用哪个函数的决定可以推迟到程序运行时。这种运行时灵活性在处理用户界面或模拟时特别有用,因为在程序执行的各个点上行为可能会改变或对象类型可能会不同。

事实上,在 C++ 中使用虚函数和继承可以增强可重用性、模块化、可扩展性和运行时灵活性。这些设计原则有助于良好的软件开发实践,从而简化了复杂系统的创建和维护过程。

结论

在 C++ 中,从派生类调用虚函数涉及创建一个具有虚函数的基类,派生一个重写虚函数的类,然后使用基类指针或引用根据运行时对象的确切类型调用虚函数。理解这些概念对于实现多态性并在 C++ 中创建灵活、可重用的代码至关重要。