编译时多态与运行时多态
本课属于 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),但处理执行时才知道类型的对象时更加灵活。
挑战
简单让我们构建一个计算器系统,并排展示两种多态。你将创建一个系统,其中编译时多态通过函数重载处理不同的输入类型,而运行时多态则允许动态切换不同的计算策略。
你将把代码分成三个文件:
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;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器