在我们之前讨论了C++继承的基础知识后,现在让我们深入探索面向对象编程中另一个强大的概念——多态与虚函数。如果说继承是建立类之间的"血缘关系",那么多态则是赋予这些关系"灵活性和适应性"的机制。通过生动的实例和实用的代码,让我们一起揭开多态的神秘面纱。
多态是面向对象编程的核心特性之一,它允许我们用同一个接口调用不同实现。就像人类社会中的"角色转换"——同一个人可能既是父亲、又是员工、还是学生,在不同场合展现不同的行为。
C++中的多态分为两种:
想象一个遥控器(基类接口),它可以控制不同的家电(派生类)。按下"开/关"按钮时,电视会开启/关闭显示屏,空调会开启/关闭制冷系统,音响会开启/关闭声音输出。虽然按钮操作相同,但产生的效果因设备而异——这就是多态的体现。
class RemoteControl {
public:
virtual void powerOn() {
std::cout << "遥控器:按下电源开关" << std::endl;
}
virtual void powerOff() {
std::cout << "遥控器:按下电源开关" << std::endl;
}
virtual ~RemoteControl() {} // 虚析构函数
};
class TV : public RemoteControl {
public:
void powerOn() override {
RemoteControl::powerOn(); // 调用基类方法
std::cout << "电视:屏幕亮起,显示频道" << std::endl;
}
void powerOff() override {
RemoteControl::powerOff();
std::cout << "电视:屏幕关闭,待机状态" << std::endl;
}
};
class AirConditioner : public RemoteControl {
public:
void powerOn() override {
RemoteControl::powerOn();
std::cout << "空调:启动压缩机,开始制冷" << std::endl;
}
void powerOff() override {
RemoteControl::powerOff();
std::cout << "空调:停止压缩机,关闭出风" << std::endl;
}
};
// 使用示例
void useDevice(RemoteControl& device) {
device.powerOn(); // 动态绑定,调用实际对象的方法
// ... 使用设备 ...
device.powerOff();
}
int main() {
TV myTV;
AirConditioner myAC;
std::cout << "=== 使用电视 ===" << std::endl;
useDevice(myTV);
std::cout << "\n=== 使用空调 ===" << std::endl;
useDevice(myAC);
return 0;
}
虚函数是实现动态多态的核心机制。通过在基类中声明虚函数,并在派生类中重写这些函数,我们可以实现基类指针或引用调用派生类函数的能力。
C++通过虚函数表(vtable)实现虚函数机制:
这有点像公司的组织架构:每个部门都有一个职责表(vtable),新员工(对象)加入时会得到这个表的副本,当需要执行某项任务时,查表找到负责人(函数实现)。
// 虚函数机制示意
class Base {
public:
virtual void function1() { std::cout << "Base::function1" << std::endl; }
virtual void function2() { std::cout << "Base::function2" << std::endl; }
void normalFunction() { std::cout << "Base::normalFunction" << std::endl; }
};
class Derived : public Base {
public:
void function1() override { std::cout << "Derived::function1" << std::endl; }
// function2没有重写,会继承Base的实现
};
int main() {
Base* ptr = new Derived();
ptr->function1(); // 调用Derived::function1
ptr->function2(); // 调用Base::function2
ptr->normalFunction(); // 调用Base::normalFunction (非虚函数)
delete ptr;
return 0;
}
C++11引入了两个新关键字来增强虚函数的安全性:
class Base {
public:
virtual void method1() { /* ... */ }
virtual void method2() final { /* ... */ } // 子类不能重写此方法
};
class Derived : public Base {
public:
void method1() override { /* ... */ } // 明确表示重写
// void method2() override { /* ... */ } // 错误:不能重写final方法
};
class Final final : public Derived { // 不能被继承的类
// ...
};
// class MoreDerived : public Final { /* ... */ }; // 错误:不能继承final类
纯虚函数是没有实现的虚函数,用= 0表示。包含至少一个纯虚函数的类称为抽象类,不能直接实例化。
抽象类就像是一份合同或规范,规定了派生类必须实现的功能,却不规定具体如何实现。
// 绘图应用中的抽象类示例
class Shape {
public:
// 纯虚函数
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual void draw() const = 0;
// 普通虚函数,提供默认实现
virtual void move(double dx, double dy) {
x += dx;
y += dy;
}
virtual ~Shape() {} // 虚析构函数
protected:
double x, y; // 形状的位置
Shape(double x = 0, double y = 0) : x(x), y(y) {}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double x, double y, double r) : Shape(x, y), radius(r) {}
double area() const override {
return 3.14159 * radius * radius;
}
double perimeter() const override {
return 2 * 3.14159 * radius;
}
void draw() const override {
std::cout << "绘制圆形:中心(" << x << "," << y
<< "),半径" << radius << std::endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double x, double y, double w, double h)
: Shape(x, y), width(w), height(h) {}
double area() const override {
return width * height;
}
double perimeter() const override {
return 2 * (width + height);
}
void draw() const override {
std::cout << "绘制矩形:左上角(" << x << "," << y
<< "),宽" << width << ",高" << height << std::endl;
}
};
多态的一个重要应用是面向接口编程,它允许我们编写更灵活、可扩展的代码。
策略模式是一个很好的多态应用示例,它允许在运行时选择算法的行为。就像打车软件可以根据需求选择不同的路线规划策略(最短路径、避开拥堵等)。
// 排序策略接口
class SortStrategy {
public:
virtual void sort(std::vector& data) = 0;
virtual ~SortStrategy() {}
};
// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:
void sort(std::vector& data) override {
std::cout << "使用冒泡排序..." << std::endl;
// 冒泡排序实现...
for (size_t i = 0; i < data.size(); i++) {
for (size_t j = 0; j < data.size() - i - 1; j++) {
if (data[j] > data[j + 1]) {
std::swap(data[j], data[j + 1]);
}
}
}
}
};
// 具体策略:快速排序
class QuickSort : public SortStrategy {
public:
void sort(std::vector& data) override {
std::cout << "使用快速排序..." << std::endl;
// 快速排序实现...
quickSort(data, 0, data.size() - 1);
}
private:
void quickSort(std::vector& data, int low, int high) {
if (low < high) {
int pivot = partition(data, low, high);
quickSort(data, low, pivot - 1);
quickSort(data, pivot + 1, high);
}
}
int partition(std::vector& data, int low, int high) {
int pivot = data[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (data[j] <= pivot) {
i++;
std::swap(data[i], data[j]);
}
}
std::swap(data[i + 1], data[high]);
return i + 1;
}
};
// 排序上下文
class Sorter {
private:
SortStrategy* strategy;
public:
Sorter(SortStrategy* strategy) : strategy(strategy) {}
~Sorter() {
delete strategy;
}
void setStrategy(SortStrategy* newStrategy) {
delete strategy;
strategy = newStrategy;
}
void performSort(std::vector& data) {
strategy->sort(data);
}
};
// 使用示例
int main() {
std::vector data = {5, 3, 8, 1, 7, 2};
// 使用冒泡排序
Sorter sorter(new BubbleSort());
sorter.performSort(data);
// 切换到快速排序
data = {5, 3, 8, 1, 7, 2}; // 重置数据
sorter.setStrategy(new QuickSort());
sorter.performSort(data);
return 0;
}
工厂模式是另一个广泛应用多态的设计模式,它将对象的创建与使用分离,增强了程序的灵活性。
// 产品接口
class Product {
public:
virtual void use() = 0;
virtual ~Product() {}
};
// 具体产品A
class ConcreteProductA : public Product {
public:
void use() override {
std::cout << "使用产品A" << std::endl;
}
};
// 具体产品B
class ConcreteProductB : public Product {
public:
void use() override {
std::cout << "使用产品B" << std::endl;
}
};
// 工厂接口
class Factory {
public:
virtual Product* createProduct() = 0;
virtual ~Factory() {}
};
// 具体工厂A
class ConcreteFactoryA : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductA();
}
};
// 具体工厂B
class ConcreteFactoryB : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductB();
}
};
// 客户端代码
void clientCode(Factory& factory) {
Product* product = factory.createProduct();
product->use();
delete product;
}
int main() {
ConcreteFactoryA factoryA;
std::cout << "客户端使用工厂A:" << std::endl;
clientCode(factoryA);
ConcreteFactoryB factoryB;
std::cout << "客户端使用工厂B:" << std::endl;
clientCode(factoryB);
return 0;
}
运行时类型识别(RTTI)是C++提供的一种在运行时确定对象类型的机制,包括dynamic_cast和typeid操作符。
dynamic_cast用于将基类指针或引用安全地转换为派生类指针或引用,失败时返回nullptr(对于指针)或抛出异常(对于引用)。
class Base {
public:
virtual ~Base() {} // 必须有虚函数才能使用dynamic_cast
};
class Derived1 : public Base {
public:
void specificMethod1() {
std::cout << "Derived1特有方法" << std::endl;
}
};
class Derived2 : public Base {
public:
void specificMethod2() {
std::cout << "Derived2特有方法" << std::endl;
}
};
void processObject(Base* obj) {
// 尝试将obj转型为Derived1*
Derived1* d1 = dynamic_cast(obj);
if (d1) {
std::cout << "对象是Derived1类型" << std::endl;
d1->specificMethod1();
} else {
// 尝试将obj转型为Derived2*
Derived2* d2 = dynamic_cast(obj);
if (d2) {
std::cout << "对象是Derived2类型" << std::endl;
d2->specificMethod2();
} else {
std::cout << "对象既不是Derived1也不是Derived2类型" << std::endl;
}
}
}
int main() {
Base* b1 = new Derived1();
Base* b2 = new Derived2();
std::cout << "处理第一个对象:" << std::endl;
processObject(b1);
std::cout << "处理第二个对象:" << std::endl;
processObject(b2);
delete b1;
delete b2;
return 0;
}
typeid操作符返回一个type_info对象的引用,可以用于获取对象的实际类型信息。
#include
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {};
int main() {
Base* b1 = new Base();
Base* b2 = new Derived();
std::cout << "b1的类型:" << typeid(*b1).name() << std::endl;
std::cout << "b2的类型:" << typeid(*b2).name() << std::endl;
// 比较类型
if (typeid(*b2) == typeid(Derived)) {
std::cout << "b2确实是Derived类型" << std::endl;
}
delete b1;
delete b2;
return 0;
}
虽然多态功能强大,但它确实带来了一些性能开销:
这有点像在公司中通过管理层转达指令(虚函数调用)与直接向员工下达指令(非虚函数调用)的区别:前者更灵活,允许不同部门根据自身情况执行任务,但沟通成本更高;后者更快捷,但缺乏适应性。
让我们通过一个游戏角色系统的例子来综合应用多态概念:
#include
#include
#include
#include
// 抽象基类:游戏角色
class GameCharacter {
protected:
std::string name;
int health;
int level;
public:
GameCharacter(const std::string& n, int h, int l)
: name(n), health(h), level(l) {}
// 纯虚函数
virtual void attack() const = 0;
virtual void defend() const = 0;
virtual void specialAbility() const = 0;
// 普通虚函数
virtual void levelUp() {
level++;
health += 10;
std::cout << name << "升级了!当前等级:" << level << std::endl;
}
void takeDamage(int damage) {
health -= damage;
if (health < 0) health = 0;
std::cout << name << "受到" << damage << "点伤害,当前生命值:" << health << std::endl;
}
bool isAlive() const {
return health > 0;
}
virtual void showStatus() const {
std::cout << "角色:" << name << ",等级:" << level << ",生命值:" << health << std::endl;
}
virtual ~GameCharacter() {}
};
// 战士类
class Warrior : public GameCharacter {
private:
int strength;
public:
Warrior(const std::string& name, int health = 150, int level = 1, int strength = 15)
: GameCharacter(name, health, level), strength(strength) {}
void attack() const override {
std::cout << "战士" << name << "挥动大剑,造成" << (strength * level) << "点物理伤害!" << std::endl;
}
void defend() const override {
std::cout << "战士" << name << "举起盾牌,物理防御增加!" << std::endl;
}
void specialAbility() const override {
std::cout << "战士" << name << "使用旋风斩,对周围敌人造成伤害!" << std::endl;
}
void levelUp() override {
GameCharacter::levelUp();
strength += 5;
std::cout << "力量增加了5,当前力量:" << strength << std::endl;
}
void showStatus() const override {
GameCharacter::showStatus();
std::cout << "力量:" << strength << std::endl;
}
};
// 法师类
class Mage : public GameCharacter {
private:
int intelligence;
int mana;
public:
Mage(const std::string& name, int health = 100, int level = 1, int intelligence = 20, int mana = 150)
: GameCharacter(name, health, level), intelligence(intelligence), mana(mana) {}
void attack() const override {
std::cout << "法师" << name << "发射火球,造成" << (intelligence * level) << "点魔法伤害!" << std::endl;
}
void defend() const override {
std::cout << "法师" << name << "创造魔法护盾,魔法防御增加!" << std::endl;
}
void specialAbility() const override {
std::cout << "法师" << name << "召唤陨石雨,对大范围敌人造成伤害!" << std::endl;
}
void levelUp() override {
GameCharacter::levelUp();
intelligence += 7;
mana += 20;
std::cout << "智力增加了7,法力增加了20,当前智力:" << intelligence << ",法力:" << mana << std::endl;
}
void castSpell(const std::string& spellName, int manaCost) {
if (mana >= manaCost) {
mana -= manaCost;
std::cout << "法师" << name << "施放法术:" << spellName << ",消耗" << manaCost << "点法力,剩余法力:" << mana << std::endl;
} else {
std::cout << "法力不足,无法施放法术!" << std::endl;
}
}
void showStatus() const override {
GameCharacter::showStatus();
std::cout << "智力:" << intelligence << ",法力:" << mana << std::endl;
}
};
// 弓箭手类
class Archer : public GameCharacter {
private:
int dexterity;
int arrows;
public:
Archer(const std::string& name, int health = 120, int level = 1, int dexterity = 18, int arrows = 30)
: GameCharacter(name, health, level), dexterity(dexterity), arrows(arrows) {}
void attack() const override {
std::cout << "弓箭手" << name << "射出箭矢,造成" << (dexterity * level) << "点穿透伤害!" << std::endl;
}
void defend() const override {
std::cout << "弓箭手" << name << "迅速闪避,回避率提高!" << std::endl;
}
void specialAbility() const override {
std::cout << "弓箭手" << name << "发动连射,快速射出多支箭矢!" << std::endl;
}
void levelUp() override {
GameCharacter::levelUp();
dexterity += 6;
arrows += 5;
std::cout << "敏捷增加了6,箭矢增加了5,当前敏捷:" << dexterity << ",箭矢:" << arrows << std::endl;
}
void showStatus() const override {
GameCharacter::showStatus();
std::cout << "敏捷:" << dexterity << ",箭矢:" << arrows << std::endl;
}
};
// 游戏场景类
class GameScene {
private:
std::vector> characters;
public:
void addCharacter(GameCharacter* character) {
characters.emplace_back(character);
}
void simulateBattle() {
std::cout << "===== 战斗开始 =====" << std::endl;
// 所有角色展示状态
for (const auto& character : characters) {
character->showStatus();
}
std::cout << "\n--- 第一回合 ---" << std::endl;
for (const auto& character : characters) {
character->attack();
}
std::cout << "\n--- 第二回合 ---" << std::endl;
for (const auto& character : characters) {
character->defend();
character->specialAbility();
}
std::cout << "\n--- 战斗结束,角色升级 ---" << std::endl;
for (auto& character : characters) {
character->levelUp();
character->showStatus();
std::cout << std::endl;
}
std::cout << "===== 战斗结束 =====" << std::endl;
}
};
int main() {
GameScene scene;
scene.addCharacter(new Warrior("张三"));
scene.addCharacter(new Mage("李四"));
scene.addCharacter(new Archer("王五"));
scene.simulateBattle();
return 0;
}
多态和虚函数是C++面向对象编程的精髓,它们让代码更加灵活、可扩展,也更符合现实世界的模型:
多态就像是生活中的"一专多能"——同一个人在不同场合扮演不同角色,同一个按钮在不同设备上产生不同效果。掌握多态,你就掌握了面向对象编程的灵魂。