一、继承:代码复用的艺术

想象你要开发一个游戏,里面有多种角色:战士、法师、牧师。如果每个角色都从头写代码,会有大量重复(比如移动、攻击等逻辑)。继承就是为了解决这个问题——让子类复用父类的属性和方法。

// 技术栈:C++  
class Character {  // 基类(父类)
protected:
    int health;
    string name;
public:
    Character(string n, int h) : name(n), health(h) {}
    void move() { cout << name << "移动了!" << endl; }
};

class Warrior : public Character {  // 派生类(子类)
private:
    int attackPower;
public:
    Warrior(string n, int h, int ap) : Character(n, h), attackPower(ap) {}
    void slash() { cout << name << "使用了劈砍,伤害:" << attackPower << endl; }
};

// 使用示例
Warrior w("阿尔萨斯", 100, 30);
w.move();    // 继承自Character
w.slash();   // 子类特有方法

注释

  • protected成员允许子类访问,而private不允许。
  • 构造函数初始化列表Character(n, h)先调用父类构造函数。

应用场景

  • 游戏角色系统
  • GUI库中的控件层级(如按钮继承自基础控件)

注意事项

  • 避免过度继承(超过3层会难以维护)。
  • 优先用组合(has-a)而非继承(is-a)来降低耦合。

二、多态:同一接口,不同行为

多态让你用父类指针调用子类方法。比如游戏里所有角色都要攻击,但战士用剑,法师用火球。

class Character {
public:
    virtual void attack() { cout << "基础攻击" << endl; }  // 虚函数
};

class Mage : public Character {
public:
    void attack() override { cout << "火球术!" << endl; }  // 重写
};

// 使用示例
Character* chars[] = {new Warrior(), new Mage()};
chars[0]->attack();  // 输出战士的攻击逻辑
chars[1]->attack();  // 输出法师的攻击逻辑

关键点

  • virtual声明虚函数,允许子类重写。
  • override(C++11)明确表示重写,避免拼写错误。

关联技术:动态绑定(运行时决定调用哪个函数)。


三、虚函数的实现原理:虚函数表(vTable)

每个含虚函数的类都有一个隐藏的虚函数表,存储函数指针。调用时通过对象内部的vptr找到表:

class Base {
public:
    virtual void func1() {}
    virtual void func2() {}
};

// 编译器生成的伪代码
struct Base_vTable {
    void (*func1)() = &Base::func1;
    void (*func2)() = &Base::func2;
};

内存布局

对象内存: [vptr][其他成员...]
vptr -> [func1地址][func2地址]

性能影响

  • 每次虚函数调用需间接寻址(约多消耗1-2个CPU周期)。
  • 虚函数不宜用于高频调用的简单函数(如循环内的计算)。

四、应用场景与最佳实践

典型场景

  1. 插件系统:通过基类接口扩展功能。
  2. GUI事件处理:按钮点击事件的不同响应。

示例:工厂模式

class Animal {
public:
    virtual void speak() = 0;  // 纯虚函数
};

class Dog : public Animal {
    void speak() override { cout << "汪汪!" << endl; }
};

Animal* createAnimal(string type) {
    if (type == "dog") return new Dog();
    // 其他类型...
}

优缺点

  • ✅ 优点:代码扩展性强,符合开闭原则。
  • ❌ 缺点:运行时开销,过度使用会导致设计复杂化。

总结
继承和多态是OOP的核心,但需权衡灵活性与性能。对于性能敏感场景,可考虑CRTP(奇异递归模板模式)等编译期多态技术。