
在C++面向对象编程中,多态(Polymorphism)是四大基本特性(封装、继承、多态、抽象)之一,它允许使用共同的接口来处理不同类型的对象,从而提高了代码的灵活性和可扩展性。多态使得同一个接口可以表现出不同的行为,这是通过继承和虚函数实现的。
多态按字面的意思就是“多种形态”。在面向对象编程中,多态指的是同一个接口(方法或函数调用)可以根据对象的实际类型表现出不同的行为。例如,动物发出声音的行为,猫和狗的实现方式不同,但可以通过同一个接口(如sound()方法)来调用。
多态在 C++ 中主要分为两种类型:
①编译时多态(静态多态)
②运行时多态(动态多态)
函数重载允许在同一作用域内定义多个同名函数,但它们的参数列表(参数类型、数量或顺序)必须不同。编译器根据调用时的实参类型和数量选择匹配的函数。
示例:函数重载
#include <iostream>
using namespace std;
// 函数重载示例
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
string add(const string& a, const string& b) {
return a + b;
}
int main() {
cout << add(1, 2) << endl; // 调用add(int, int)
cout << add(1.5, 2.5) << endl; // 调用add(double, double)
cout << add("Hello", " World") << endl; // 调用add(string, string)
return 0;
}
函数重载的关键点:
运算符重载允许自定义类型使用 C++ 内置运算符(如+, -, *, /等),增强代码的可读性和直观性。
示例:复数类的运算符重载
#include <iostream>
using namespace std;
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载加法运算符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 重载输出流运算符
friend ostream& operator<<(ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os;
}
};
int main() {
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2; // 调用operator+
cout << "c1: " << c1 << endl;
cout << "c2: " << c2 << endl;
cout << "c1 + c2: " << c3 << endl;
return 0;
}
运算符重载的关键点:
.、::、?:等)不能重载虚函数是在基类中声明为virtual的函数,派生类可以通过相同的函数签名(函数名、参数列表、返回类型)重写(Override)该函数。通过基类指针或引用调用虚函数时,会在运行时根据对象的实际类型决定调用哪个版本的函数。
示例:动物类的虚函数
#include <iostream>
using namespace std;
class Animal {
public:
virtual void speak() { // 虚函数
cout << "Animal speaks" << endl;
}
};
class Dog : public Animal {
public:
void speak() override { // 重写基类虚函数
cout << "Dog barks" << endl;
}
};
class Cat : public Animal {
public:
void speak() override { // 重写基类虚函数
cout << "Cat meows" << endl;
}
};
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->speak(); // 输出: Dog barks
animal2->speak(); // 输出: Cat meows
delete animal1;
delete animal2;
return 0;
}
虚函数的关键点:
virtual关键字声明override关键字(C++11 及以后)显式标记重写动态绑定是运行时多态的核心机制,它允许在运行时根据对象的实际类型来决定调用哪个函数。动态绑定通过虚函数表(VTable)和虚表指针(VPTR)实现。
虚函数表与虚表指针:
示例:动态绑定机制
class Base {
public:
virtual void func1() { cout << "Base::func1" << endl; }
virtual void func2() { cout << "Base::func2" << endl; }
};
class Derived : public Base {
public:
void func1() override { cout << "Derived::func1" << endl; }
};
int main() {
Base* ptr = new Derived();
ptr->func1(); // 动态绑定:调用Derived::func1
ptr->func2(); // 动态绑定:调用Base::func2
delete ptr;
return 0;
}
纯虚函数是在基类中声明但不提供实现的虚函数,用= 0语法表示。包含纯虚函数的类称为抽象基类(Abstract Base Class),不能实例化。派生类必须实现所有纯虚函数才能成为具体类。
示例:抽象基类与纯虚函数
#include <iostream>
using namespace std;
class Shape {
public:
virtual double area() const = 0; // 纯虚函数
virtual void draw() const = 0; // 纯虚函数
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { // 实现纯虚函数
return 3.14 * radius * radius;
}
void draw() const override { // 实现纯虚函数
cout << "Drawing a circle" << endl;
}
};
int main() {
// Shape s; // 错误:抽象基类不能实例化
Shape* shape = new Circle(5);
cout << "Area: " << shape->area() << endl;
shape->draw();
delete shape;
return 0;
}
抽象基类的关键点:
C++ 允许派生类虚函数的返回类型是基类虚函数返回类型的指针或引用,这种特性称为协变返回类型。
示例:协变返回类型
#include <iostream>
#include <typeinfo>
using namespace std;
// 基类:添加虚析构函数
class Base {
public:
virtual Base* clone() {
cout << "Base::clone" << endl;
return new Base();
}
virtual ~Base() {} // 虚析构函数
};
// 派生类:添加析构函数(若有资源需释放)
class Derived : public Base {
public:
Derived* clone() override { // 协变返回类型
cout << "Derived::clone" << endl;
return new Derived();
}
~Derived() override { // 可选:若有资源需释放,重写析构函数
cout << "Derived::~Derived" << endl;
}
};
int main() {
Base* basePtr = new Derived();
// 调用 clone() 返回 Base*(运行时实际是 Derived*)
Base* clonedBase = basePtr->clone();
// 使用 dynamic_cast 安全转换为 Derived*
Derived* derivedPtr = dynamic_cast<Derived*>(clonedBase);
if (derivedPtr) {
cout << "Successfully converted to Derived*" << endl;
} else {
cerr << "Error: cloned object is not of type Derived" << endl;
}
// 释放内存(虚析构函数确保正确调用派生类析构)
delete basePtr;
delete clonedBase; // 或 delete derivedPtr(若转换成功)
return 0;
}
当通过基类指针删除派生类对象时,如果基类析构函数不是虚函数,只会调用基类的析构函数,导致派生类部分资源无法释放,造成内存泄漏。因此,基类的析构函数通常应声明为虚函数。
示例:虚析构函数的必要性
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { // 虚析构函数
cout << "Base destructor" << endl;
}
};
class Derived : public Base {
private:
int* data;
public:
Derived() {
data = new int[100];
cout << "Derived constructor" << endl;
}
~Derived() override {
delete[] data;
cout << "Derived destructor" << endl;
}
};
int main() {
Base* ptr = new Derived();
delete ptr; // 输出:Derived destructor → Base destructor
return 0;
}
纯虚析构函数允许将类定义为抽象基类,同时必须为其提供实现(因为所有析构函数都会在对象销毁时被调用)。
示例:纯虚析构函数
class Base {
public:
virtual ~Base() = 0; // 纯虚析构函数
};
// 必须提供纯虚析构函数的实现
Base::~Base() {
cout << "Base pure virtual destructor" << endl;
}
class Derived : public Base {
public:
~Derived() override {
cout << "Derived destructor" << endl;
}
};虚函数的默认参数值是在编译时确定的,而不是运行时。因此,通过基类指针或引用调用虚函数时,默认参数值由指针或引用的静态类型决定,而非对象的实际类型。
示例:虚函数与默认参数
#include <iostream>
using namespace std;
class Base {
public:
virtual void print(int x = 10) {
cout << "Base::print x = " << x << endl;
}
};
class Derived : public Base {
public:
void print(int x = 20) override {
cout << "Derived::print x = " << x << endl;
}
};
int main() {
Base* ptr = new Derived();
ptr->print(); // 输出:Derived::print x = 10(默认参数来自Base)
delete ptr;
return 0;
}
策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。
示例:策略模式实现
#include <iostream>
using namespace std;
// 策略接口
class SortStrategy {
public:
virtual void sort() = 0;
virtual ~SortStrategy() {}
};
// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:
void sort() override {
cout << "Sorting using Bubble Sort" << endl;
}
};
// 具体策略:快速排序
class QuickSort : public SortStrategy {
public:
void sort() override {
cout << "Sorting using Quick Sort" << endl;
}
};
// 上下文类
class Sorter {
private:
SortStrategy* strategy;
public:
Sorter(SortStrategy* s) : strategy(s) {}
~Sorter() { delete strategy; }
void setStrategy(SortStrategy* s) {
delete strategy;
strategy = s;
}
void performSort() {
strategy->sort();
}
};
int main() {
Sorter sorter(new BubbleSort());
sorter.performSort(); // 输出:Sorting using Bubble Sort
sorter.setStrategy(new QuickSort());
sorter.performSort(); // 输出:Sorting using Quick Sort
return 0;
}
工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离。通过工厂模式,可以根据不同的条件创建不同类型的对象。
示例:工厂模式实现
#include <iostream>
#include <string>
using namespace std;
// 产品基类
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
// 具体产品:圆形
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing a circle" << endl;
}
};
// 具体产品:矩形
class Rectangle : public Shape {
public:
void draw() override {
cout << "Drawing a rectangle" << endl;
}
};
// 工厂类
class ShapeFactory {
public:
static Shape* createShape(const string& type) {
if (type == "circle") {
return new Circle();
} else if (type == "rectangle") {
return new Rectangle();
}
return nullptr;
}
};
int main() {
Shape* circle = ShapeFactory::createShape("circle");
circle->draw(); // 输出:Drawing a circle
Shape* rectangle = ShapeFactory::createShape("rectangle");
rectangle->draw(); // 输出:Drawing a rectangle
delete circle;
delete rectangle;
return 0;
}
通过基类指针或智能指针,可以在容器中存储不同类型的派生类对象,实现多态容器。
示例:多态容器
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Animal {
public:
virtual void speak() {
cout << "Animal speaks" << endl;
}
virtual ~Animal() {}
};
class Dog : public Animal {
public:
void speak() override {
cout << "Dog barks" << endl;
}
};
class Cat : public Animal {
public:
void speak() override {
cout << "Cat meows" << endl;
}
};
int main() {
// 使用智能指针的多态容器
vector<unique_ptr<Animal>> animals;
animals.push_back(make_unique<Dog>());
animals.push_back(make_unique<Cat>());
// 遍历容器,调用多态方法
for (const auto& animal : animals) {
animal->speak();
}
return 0;
}
继承是多态的基础。通过继承,子类可以重写父类的虚函数,从而使得在运行时根据对象类型调用正确的函数。没有继承,多态就无法实现。
封装是指将数据和操作数据的方法绑定在一起,并隐藏对象的内部实现细节,仅暴露必要的接口。多态与封装相结合,可以使得程序更加模块化和易于维护。通过封装,可以隐藏对象的内部状态和行为,而通过多态,可以使得程序能够以统一的方式处理不同类型的对象。
以下是一个结合了继承、封装和多态的示例,展示了它们之间的关系:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 基类 Animal(封装:隐藏内部实现细节)
class Animal {
private:
string name;
public:
Animal(const string& n) : name(n) {}
string getName() const {
return name;
}
virtual void sound() const = 0; // 纯虚函数,多态的基础
virtual ~Animal() {} // 虚析构函数
};
// 派生类 Dog(继承和多态)
class Dog : public Animal {
public:
Dog(const string& n) : Animal(n) {}
void sound() const override {
cout << getName() << " says: Woof! Woof!" << endl;
}
};
// 派生类 Cat(继承和多态)
class Cat : public Animal {
public:
Cat(const string& n) : Animal(n) {}
void sound() const override {
cout << getName() << " says: Meow! Meow!" << endl;
}
};
// 派生类 Bird(继承和多态)
class Bird : public Animal {
public:
Bird(const string& n) : Animal(n) {}
void sound() const override {
cout << getName() << " says: Chirp! Chirp!" << endl;
}
};
// 测试多态
void makeAnimalSound(const Animal* animal) {
animal->sound(); // 动态绑定:根据对象的实际类型调用相应的sound方法
}
int main() {
vector<Animal*> animals;
animals.push_back(new Dog("Buddy"));
animals.push_back(new Cat("Whiskers"));
animals.push_back(new Bird("Tweety"));
for (Animal* animal : animals) {
makeAnimalSound(animal); // 动态绑定:根据对象的实际类型调用相应的sound方法
}
// 释放内存
for (Animal* animal : animals) {
delete animal;
}
return 0;
}
运行时多态(虚函数)会带来一定的性能开销:
下面是一个综合应用多态的示例,实现一个简单的图形绘制系统:
#include <iostream>
#include <vector>
#include <memory> // 包含 unique_ptr
#include <cmath>
using namespace std;
// ====================== 抽象基类:图形 ======================
class Shape {
public:
virtual string getName() const = 0;
virtual double getArea() const = 0;
virtual void draw() const = 0;
virtual ~Shape() {} // 虚析构函数确保多态释放
};
// ====================== 二维图形基类 ======================
class TwoDShape : public Shape {
public:
double getArea() const override = 0;
};
// ====================== 三维图形基类 ======================
class ThreeDShape : public Shape {
public:
virtual double getVolume() const = 0;
double getArea() const override { return getSurfaceArea(); }
virtual double getSurfaceArea() const = 0;
};
// ====================== 派生类:圆形 ======================
class Circle : public TwoDShape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
string getName() const override { return "Circle"; }
double getArea() const override { return M_PI * radius * radius; }
void draw() const override {
cout << "Drawing a circle with radius " << radius << endl;
}
};
// ====================== 派生类:矩形 ======================
class Rectangle : public TwoDShape {
private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
string getName() const override { return "Rectangle"; }
double getArea() const override { return width * height; }
void draw() const override {
cout << "Drawing a rectangle with width " << width
<< " and height " << height << endl;
}
};
// ====================== 派生类:球体 ======================
class Sphere : public ThreeDShape {
private:
double radius;
public:
Sphere(double r) : radius(r) {}
string getName() const override { return "Sphere"; }
double getVolume() const override { return (4.0/3.0) * M_PI * pow(radius, 3); }
double getSurfaceArea() const override { return 4 * M_PI * radius * radius; }
void draw() const override {
cout << "Drawing a sphere with radius " << radius << endl;
}
};
// ====================== 派生类:立方体 ======================
class Cube : public ThreeDShape {
private:
double side;
public:
Cube(double s) : side(s) {}
string getName() const override { return "Cube"; }
double getVolume() const override { return pow(side, 3); }
double getSurfaceArea() const override { return 6 * side * side; }
void draw() const override {
cout << "Drawing a cube with side length " << side << endl;
}
};
// ====================== 图形管理器 ======================
class ShapeManager {
private:
vector<unique_ptr<Shape>> shapes;
public:
void addShape(unique_ptr<Shape> shape) {
shapes.push_back(move(shape)); // 转移所有权
}
void drawAll() const {
for (const auto& shape : shapes) {
shape->draw();
cout << "Name: " << shape->getName()
<< ", Area: " << shape->getArea();
// 动态类型转换判断是否为三维图形
if (auto* threeDShape = dynamic_cast<const ThreeDShape*>(shape.get())) {
cout << ", Volume: " << threeDShape->getVolume();
}
cout << endl << "------------------------" << endl;
}
}
};
// ====================== 主函数 ======================
int main() {
ShapeManager manager;
// C++11 兼容写法:手动用 new 创建对象,传递给 unique_ptr
manager.addShape(unique_ptr<Shape>(new Circle(5)));
manager.addShape(unique_ptr<Shape>(new Rectangle(4, 6)));
manager.addShape(unique_ptr<Shape>(new Sphere(3)));
manager.addShape(unique_ptr<Shape>(new Cube(2)));
// 绘制所有图形并显示信息
manager.drawAll();
return 0;
}
override和final关键字override关键字:用于检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。有助于避免由于疏忽而导致的函数名拼写错误等问题。final关键字:用于修饰虚函数,表示该虚函数不能再被重写;也可以用于修饰类,表示该类不能被继承。多态是 C++ 面向对象编程的核心特性之一,它通过统一的接口实现不同的行为,使代码更加灵活、可扩展和可维护。编译时多态通过函数重载和运算符重载实现,运行时多态通过虚函数和动态绑定实现。在设计复杂系统时,合理应用多态可以显著提高代码的质量和可维护性。
关键知识点回顾:
通过深入理解和掌握多态机制,将能够设计出更加优雅、灵活和可扩展的 C++ 程序。