多态是面向对象编程中的一种重要概念,它允许不同的对象以各自独特的方式对同一消息进行响应。多态性是继承和虚函数的必然结果,它使得程序更加灵活和可扩展,能够提高代码的可维护性和可重用性。
可扩展性: 在面向对象编程中,多态可以提高代码的可扩展性。通过继承,子类可以扩展或重写父类的功能,而多态性使得程序能够以同一种方式处理不同的子类对象。这样,当需要增加新的子类时,只需要定义新的子类,而无需修改已有代码,从而提高了代码的可扩展性。
例一:
class Shape {
public:
virtual void Draw() = 0;
};
class Circle : public Shape {
public:
void Draw() override { cout << "Draw Circle" << endl; }
};
class Square : public Shape {
public:
void Draw() override { cout << "Draw Square" << endl; }
};
void DrawAllShapes(vector<Shape*>& shapes) {
for (auto s : shapes) {
s->Draw();
}
}
int main() {
vector<Shape*> shapes;
shapes.push_back(new Circle());
shapes.push_back(new Square());
DrawAllShapes(shapes);
return 0;
}
在例一中,Shape是一个抽象基类,它定义了Draw()纯虚函数。Circle和Square是Shape的具体子类,并分别实现了Draw()函数。在DrawAllShapes()函数中,通过遍历shapes数组并调用每个元素的Draw()函数,实现了不同形状的绘制,而不需要修改已有的代码。
可维护性: 多态也可以提高代码的可维护性。当需要修改某个功能时,只需要修改相关的类的实现,而不需要修改调用该功能的代码。这样可以保证程序的其他部分不会受到影响。
可重用性: 多态还可以提高代码的可重用性。多态允许我们将父类指针或引用指向子类对象,从而能够方便地复用代码。这样,我们可以使用同样的代码处理不同的对象,并且可以在不同的上下文中重用相同的代码。
例二:
class Animal {
public:
virtual void Speak() = 0;
};
class Dog : public Animal {
public:
void Speak() override { cout << "Bark!" << endl; }
};
class Cat : public Animal {
public:
void Speak() override { cout << "Meow!" << endl; }
};
void MakeAnimalSpeak(Animal* animal) {
animal->Speak();
}
int main() {
Animal* dog = new Dog();
Animal* cat = new Cat();
MakeAnimalSpeak(dog);
MakeAnimalSpeak(cat);
return 0;
}
在例二中,我们定义了一个抽象基类`Animal`,并派生出两个子类`Dog`和`Cat`。`Animal`类有一个纯虚函数`Speak`,该函数需要在派生类中被实现。`Dog`类和`Cat`类分别重写了`Speak`函数,并分别输出`"Bark!"`和`"Meow!"`。
主函数中,通过`new`关键字创建一个`Dog`对象和一个`Cat`对象,分别赋给基类指针`Animal* dog`和`Animal* cat`。然后,通过调用`MakeAnimalSpeak`函数,将这两个指针传递给该函数进行处理。在`MakeAnimalSpeak`函数内部,调用了传入对象的`Speak`函数,由于在运行时进行虚函数绑定,因此将会分别输出`"Bark!"`和`"Meow!"`。
这种通过基类指针或引用调用派生类对象的方式称为多态。多态性使得可以通过基类接口来操作派生类对象,从而简化了程序设计,提高了代码的可扩展性和可维护性。
再举一个例子,假设我们有一个图形类 Shape
,有多个派生类如矩形 Rectangle
、圆形 Circle
、三角形 Triangle
等,它们都有各自的计算面积和周长的方法。
如果我们使用静态多态(函数重载),则每个图形都需要写自己的面积和周长计算函数,并在使用时手动选择合适的函数。代码可能会像这样:
class Shape {
public:
virtual double area() = 0;
virtual double perimeter() = 0;
};
class Rectangle : public Shape {
public:
double area() override { /* 计算矩形面积 */ }
double perimeter() override { /* 计算矩形周长 */ }
};
class Circle : public Shape {
public:
double area() override { /* 计算圆形面积 */ }
double perimeter() override { /* 计算圆形周长 */ }
};
class Triangle : public Shape {
public:
double area() override { /* 计算三角形面积 */ }
double perimeter() override { /* 计算三角形周长 */ }
};
// 在使用时需要手动选择合适的函数
void printShapeInfo(Shape* shape) {
if (dynamic_cast<Rectangle*>(shape)) {
std::cout << "矩形,面积:" << shape->area() << ",周长:" << shape->perimeter() << std::endl;
} else if (dynamic_cast<Circle*>(shape)) {
std::cout << "圆形,面积:" << shape->area() << ",周长:" << shape->perimeter() << std::endl;
} else if (dynamic_cast<Triangle*>(shape)) {
std::cout << "三角形,面积:" << shape->area() << ",周长:" << shape->perimeter() << std::endl;
}
}
使用动态多态(虚函数重写)的话,我们只需要在基类中声明虚函数,然后在每个派生类中重写该函数,即可实现多态特性,使得代码更加灵活和可扩展,如下所示:
class Shape {
public:
virtual double area() = 0;
virtual double perimeter() = 0;
virtual ~Shape() = default; // 声明虚析构函数,以确保正确销毁派生类对象
};
class Rectangle : public Shape {
public:
double area() override { /* 计算矩形面积 */ }
double perimeter() override { /* 计算矩形周长 */ }
};
class Circle : public Shape {
public:
double area() override { /* 计算圆形面积 */ }
double perimeter() override { /* 计算圆形周长 */ }
};
class Triangle : public Shape {
public:
double area() override { /* 计算三角形面积 */ }
double perimeter() override { /* 计算三角形周长 */ }
};
// 使用动态多态调用虚函数,无需手动选择合适的函数
void printShapeInfo(Shape* shape) {
std::cout<< "Shape area: " << shape->area() << std::endl;
std::cout << "Shape perimeter: " << shape->perimeter() << std::endl;
}
int main() {
// 创建不同形状的对象,传递给printShapeInfo函数进行打印
Rectangle rect;
Circle circle;
Triangle tri;
printShapeInfo(&rect);
printShapeInfo(&circle);
printShapeInfo(&tri);
return 0;
}
以上代码中,定义了一个基类Shape
,并声明了两个纯虚函数area()
和perimeter()
,派生类Rectangle
、Circle
和Triangle
分别重写了这两个函数,从而实现了自己特定形状的计算。在printShapeInfo()
函数中,传入一个指向Shape
对象的指针,通过虚函数机制实现动态多态,调用具体派生类实现的area()
和perimeter()
函数,无需手动选择合适的函数。这种方式提高了代码的可扩展性和可维护性,可以轻松地添加新的派生类实现新的形状,同时也提高了代码的可重用性,可以在不同的上下文中使用Shape
对象进行计算。