Menu
CoddyTech

编译时多态与运行时多态

本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 56 课,共 104 课。

多态的意思是“多种形式”。它是面向对象编程的核心概念之一,允许以统一的方式处理对象,同时让它们表现出不同的行为。C++ 支持两种不同类型的多态,它们分别在程序执行的不同阶段解析。

编译时多态(也称为静态多态)由编译器在程序运行前解析。编译器根据函数签名确定要调用的具体函数。这包括函数重载和模板:

void print(int x) { std::cout << "Integer: " << x << std::endl; }
void print(double x) { std::cout << "Double: " << x << std::endl; }

print(5);      // 编译器选择 print(int)
print(3.14);   // 编译器选择 print(double)

运行时多态(也称为动态多态)在程序运行时确定。调用哪个函数取决于对象的实际类型,而不是指针或引用的类型。这是通过虚函数实现的:

class Shape {
public:
    virtual void draw() { std::cout << "Drawing shape" << std::endl; }
};

class Circle : public Shape {
public:
    void draw() override { std::cout << "Drawing circle" << std::endl; }
};

Shape* s = new Circle();
s->draw();  // 运行时决定:“Drawing circle”

关键的权衡是:编译时多态没有运行时开销,因为决策在编译期间就已作出;而运行时多态会带来少量开销(查找 vtable),但处理执行时才知道类型的对象时更加灵活。

challenge icon

挑战

简单

让我们构建一个计算器系统,并排展示两种多态。你将创建一个系统,其中编译时多态通过函数重载处理不同的输入类型,而运行时多态则允许动态切换不同的计算策略。

你将把代码分成三个文件:

  • Calculator.h:定义一个基类 Calculator,用于表示任意计算策略:
    • 一个虚拟 calculate(int a, int b) 方法,返回 int,并打印:Base calculation: <a> ? <b>(返回 0)
    • 一个虚析构函数
  • Operations.h:定义两个派生计算器类,重写计算行为:
    • Adder:重写 calculate(),打印 Adding: <a> + <b> 并返回总和
    • Multiplier:重写 calculate(),打印 Multiplying: <a> * <b> 并返回乘积
    两个类都应使用 override 关键字。
  • main.cpp:创建一个展示两种多态的系统。读取两个整数输入(每个输入各占一行)。

    首先,通过创建三个重载的 display() 函数来展示编译时多态:

    • display(int x) 打印:Integer value: <x>
    • display(double x) 打印:Double value: <x>
    • display(const std::string& x) 打印:String value: <x>

    然后,通过创建一个 Calculator* 指针数组来展示运行时多态,其中包含一个基类 Calculator、一个 Adder 和一个 Multiplier。遍历数组,对每个对象使用你的输入值调用 calculate(),并在每次计算后打印结果。

    按以下格式组织输出:

    === Compile-Time Polymorphism ===
    <display outputs for int, double, string>
    
    === Runtime Polymorphism ===
    <calculate outputs with results>

    对于编译时部分,依次使用第一个输入调用 display():先作为整数,再作为加上 .5 的双精度数,最后作为字符串 "Result"。完成后,清理动态分配的计算器。

例如,输入 10 和 3 时:

=== Compile-Time Polymorphism ===
Integer value: 10
Double value: 10.5
String value: Result

=== Runtime Polymorphism ===
Base calculation: 10 ? 3
Result: 0
Adding: 10 + 3
Result: 13
Multiplying: 10 * 3
Result: 30

注意,编译器会根据参数类型选择正确的 display() 重载(编译时决定),而正确的 calculate() 方法则会在运行时通过虚函数表机制,根据实际对象类型确定。

亲手试试

#include <iostream>
#include <string>
#include "Calculator.h"
#include "Operations.h"

// TODO:创建 3 个重载的 display() 函数:
// 1. display(int x):输出 "Integer value: <x>"
// 2. display(double x):输出 "Double value: <x>"
// 3. display(const std::string& x):输出 "String value: <x>"



int main() {
    // 读取两个整数输入
    int a, b;
    std::cin >> a;
    std::cin >> b;
    
    // === Compile-Time Polymorphism ===
    std::cout << "=== Compile-Time Polymorphism ===" << std::endl;
    // TODO:用以下参数调用 display():
    // 将 a 作为整数
    // 将 a 作为 double 类型的值(加上 0.5)
    // 使用字符串 "Result"
    
    
    std::cout << std::endl;
    
    // === Runtime Polymorphism ===
    std::cout << "=== Runtime Polymorphism ===" << std::endl;
    // TODO:创建一个包含 3 个元素的 Calculator* 指针数组:
    // 一个基类 Calculator 对象
    // 一个 Adder 对象
    // - 一个 Multiplier 对象
    
    // TODO:遍历数组,对每个元素调用 calculate(a, b),
    // 并在每次计算后打印 "Result: <return_value>"
    
    
    // TODO:释放动态分配的内存
    
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器