多态性(Polymorphism)是面向对象编程中的一个核心概念,它允许不同类的对象通过相同的接口调用不同的实现。它使得可以通过基类指针或引用来处理派生类对象,从而实现灵活和可扩展的设计。
多态性的类型
- 编译时多态性(静态多态性):
- 函数重载:同一个函数名根据参数不同可以有不同的实现。
- 运算符重载:允许自定义运算符以对自定义类型进行操作。
class Print {
public:
void display(int i) {
std::cout << "Integer: " << i << std::endl;
}
void display(double d) {
std::cout << "Double: " << d << std::endl;
}
};
Print p;
p.display(5); // 调用 display(int)
p.display(5.5); // 调用 display(double)
- 运行时多态性(动态多态性):
- 虚函数:允许基类指针或引用调用派生类中的实现。这是通过虚函数表(vtable)和动态绑定(dynamic dispatch)机制实现的。
- 纯虚函数和抽象类:纯虚函数使类成为抽象类,无法直接实例化,但可以用来定义接口供派生类实现。
class Base {
public:
virtual void show() {
std::cout << "Base show" << std::endl;
}
virtual ~Base() = default;
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived show" << std::endl;
}
};
void display(Base* obj) {
obj->show(); // 调用派生类的 show 实现
}
int main() {
Base* b = new Derived;
display(b); // 输出:Derived show
delete b;
return 0;
}
详细解释
1. 虚函数(Virtual Functions
虚函数是实现运行时多态性的关键。基类中的虚函数可以被派生类重写,派生类的重写版本会在运行时被调用,而不是基类中的实现。通过在基类中将函数声明为虚函数(使用 virtual 关键字),你告诉编译器在运行时进行动态绑定。
- 虚函数声明:在基类中声明为 virtual。
- 函数重写:在派生类中重写时,使用 override 关键字(可选,但推荐)。
2. 纯虚函数和抽象类
纯虚函数是没有实现的虚函数,它在基类中声明为 = 0,使得类变成抽象类。抽象类不能被实例化,但可以作为接口使用,派生类必须实现所有纯虚函数才能实例化。
class AbstractBase {
public:
virtual void pureVirtualFunction() = 0; // 纯虚函数
};
3. 多态性的优势
- 代码复用:通过基类接口可以重用代码而不需要知道具体的派生类实现。
- 灵活性和扩展性:可以在不修改现有代码的情况下,添加新的派生类和功能。
- 接口一致性:通过基类接口,可以对不同的派生类对象进行一致的操作。
示例应用
假设你有一个图形绘制程序,你可以定义一个基类 Shape 和几个派生类 Circle、Rectangle 等,每个派生类实现其自己的 draw 函数。
class Shape {
public:
virtual void draw() const = 0; // 纯虚函数
};
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing Circle" << std::endl;
}
};
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "Drawing Rectangle" << std::endl;
}
};
void render(Shape* shape) {
shape->draw(); // 动态绑定到实际的 draw 实现
}
int main() {
Circle c;
Rectangle r;
render(&c); // 输出:Drawing Circle
render(&r); // 输出:Drawing Rectangle
return 0;
}
总结
- 多态性 允许使用基类指针或引用来处理不同的派生类对象,通过相同的接口实现不同的行为。
- 虚函数 是实现运行时多态性的基础,通过动态绑定决定调用哪个派生类的方法。
- 纯虚函数 和 抽象类 允许定义接口,强制派生类提供具体实现,从而实现灵活和可扩展的设计。