一、继承:代码复用的艺术
想象你要开发一个游戏,里面有多种角色:战士、法师、牧师。如果每个角色都从头写代码,会有大量重复(比如移动、攻击等逻辑)。继承就是为了解决这个问题——让子类复用父类的属性和方法。
// 技术栈: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周期)。
- 虚函数不宜用于高频调用的简单函数(如循环内的计算)。
四、应用场景与最佳实践
典型场景:
- 插件系统:通过基类接口扩展功能。
- 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(奇异递归模板模式)等编译期多态技术。
评论