多态按字面的意思就是多种形态。
当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
在 C++ 中,多态(Polymorphism)是面向对象编程的重要特性之一。
C++ 多态允许使用基类指针或引用来调用子类的重写方法,从而使得同一接口可以表现不同的行为。
多态使得代码更加灵活和通用,程序可以通过基类指针或引用来操作不同类型的对象,而不需要显式区分对象类型。这样可以使代码更具扩展性,在增加新的形状类时不需要修改主程序。
以下是多态的几个关键点:
虚函数(Virtual Functions):
动态绑定(Dynamic Binding):
纯虚函数(Pure Virtual Functions):
多态的实现机制:
使用多态的优势:
注意事项:
实例 1
我们通过一个简单的实例来了解多态的应用:
#include <iostream> using namespace std; // 基类 Animal class Animal { public: // 虚函数 sound,为不同的动物发声提供接口 virtual void sound() const { cout << "Animal makes a sound" << endl; } // 虚析构函数确保子类对象被正确析构 virtual ~Animal() { cout << "Animal destroyed" << endl; } }; // 派生类 Dog,继承自 Animal class Dog : public Animal { public: // 重写 sound 方法 void sound() const override { cout << "Dog barks" << endl; } ~Dog() { cout << "Dog destroyed" << endl; } }; // 派生类 Cat,继承自 Animal class Cat : public Animal { public: // 重写 sound 方法 void sound() const override { cout << "Cat meows" << endl; } ~Cat() { cout << "Cat destroyed" << endl; } }; // 测试多态 int main() { Animal* animalPtr; // 基类指针 // 创建 Dog 对象,并指向 Animal 指针 animalPtr = new Dog(); animalPtr->sound(); // 调用 Dog 的 sound 方法 delete animalPtr; // 释放内存,调用 Dog 和 Animal 的析构函数 // 创建 Cat 对象,并指向 Animal 指针 animalPtr = new Cat(); animalPtr->sound(); // 调用 Cat 的 sound 方法 delete animalPtr; // 释放内存,调用 Cat 和 Animal 的析构函数 return 0; }
程序执行输出为:
Dog barks Dog destroyed Animal destroyed Cat meows Cat destroyed Animal destroyed
代码解释
基类 Animal:
派生类 Dog 和 Cat:
主函数 main():
关键概念
实例 2
下面的实例中,我们通过多态实现了一个通用的 Shape 基类和两个派生类 Rectangle 和 Triangle。
通过基类指针调用不同的派生类方法,展示了多态的动态绑定特性。
#include <iostream> using namespace std; // 基类 Shape,表示形状 class Shape { protected: int width, height; // 宽度和高度 public: // 构造函数,带有默认参数 Shape(int a = 0, int b = 0) : width(a), height(b) { } // 虚函数 area,用于计算面积 // 使用 virtual 关键字,实现多态 virtual int area() { cout << "Shape class area: " << endl; return 0; } }; // 派生类 Rectangle,表示矩形 class Rectangle : public Shape { public: // 构造函数,使用基类构造函数初始化 width 和 height Rectangle(int a = 0, int b = 0) : Shape(a, b) { } // 重写 area 函数,计算矩形面积 int area() override { cout << "Rectangle class area: " << endl; return width * height; } }; // 派生类 Triangle,表示三角形 class Triangle : public Shape { public: // 构造函数,使用基类构造函数初始化 width 和 height Triangle(int a = 0, int b = 0) : Shape(a, b) { } // 重写 area 函数,计算三角形面积 int area() override { cout << "Triangle class area: " << endl; return (width * height / 2); } }; // 主函数 int main() { Shape *shape; // 基类指针 Rectangle rec(10, 7); // 矩形对象 Triangle tri(10, 5); // 三角形对象 // 将基类指针指向矩形对象,并调用 area 函数 shape = &rec; cout << "Rectangle Area: " << shape->area() << endl; // 将基类指针指向三角形对象,并调用 area 函数 shape = &tri; cout << "Triangle Area: " << shape->area() << endl; return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Rectangle Area: Rectangle class area: 70 Triangle Area: Triangle class area: 25
代码分析
Shape 类的定义:
// 基类 Shape,表示形状 class Shape { protected: int width, height; // 宽度和高度 public: // 构造函数,带有默认参数 Shape(int a = 0, int b = 0) : width(a), height(b) { } // 虚函数 area,用于计算面积 virtual int area() { cout << "Shape class area: " << endl; return 0; } };
Rectangle 类的定义:
// 派生类 Rectangle,表示矩形 class Rectangle : public Shape { public: // 构造函数,使用基类构造函数初始化 width 和 height Rectangle(int a = 0, int b = 0) : Shape(a, b) { } // 重写 area 函数,计算矩形面积 int area() override { cout << "Rectangle class area: " << endl; return width * height; } };
Triangle 类的定义:
// 派生类 Triangle,表示三角形 class Triangle : public Shape { public: // 构造函数,使用基类构造函数初始化 width 和 height Triangle(int a = 0, int b = 0) : Shape(a, b) { } // 重写 area 函数,计算三角形面积 int area() override { cout << "Triangle class area: " << endl; return (width * height / 2); } };
主函数中的多态行为:
// 主函数 int main() { Shape *shape; // 基类指针 Rectangle rec(10, 7); // 矩形对象 Triangle tri(10, 5); // 三角形对象 // 将基类指针指向矩形对象,并调用 area 函数 shape = &rec; cout << "Rectangle Area: " << shape->area() << endl; // 将基类指针指向三角形对象,并调用 area 函数 shape = &tri; cout << "Triangle Area: " << shape->area() << endl; return 0; }
虚函数
虚函数是在基类中使用关键字 virtual 声明的函数。
虚函数允许子类重写它,从而在运行时通过基类指针或引用调用子类的重写版本,实现动态绑定。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
特点:
#include <iostream> using namespace std; class Animal { public: virtual void sound() { // 虚函数 cout << "Animal makes a sound" << endl; } }; class Dog : public Animal { public: void sound() override { // 重写虚函数 cout << "Dog barks" << endl; } }; int main() { Animal *animal = new Dog(); animal->sound(); // 输出: Dog barks delete animal; }
以上代码中,sound 是 Animal 类的虚函数。通过 Animal* 指针 animal 调用 sound() 时,程序会根据实际对象类型(Dog)来选择调用 Dog::sound()。
纯虚函数
您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
纯虚函数是没有实现的虚函数,在基类中用 = 0 来声明。
纯虚函数表示基类定义了一个接口,但具体实现由派生类负责。
纯虚函数使得基类变为抽象类(abstract class),无法实例化。
我们可以把基类中的虚函数 area() 改写如下:
#include <iostream> using namespace std; class Shape { public: virtual int area() = 0; // 纯虚函数,强制子类实现此方法 }; class Rectangle : public Shape { private: int width, height; public: Rectangle(int w, int h) : width(w), height(h) { } int area() override { // 实现纯虚函数 return width * height; } }; int main() { Shape *shape = new Rectangle(10, 5); cout << "Rectangle Area: " << shape->area() << endl; // 输出: Rectangle Area: 50 delete shape; }
= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
虚函数与纯虚函数的对比
virtual
= 0