C++之多态

一、什么是多态

多态(Polymorphism) 是面向对象程序设计中最重要的特征之一。 它指的是:

"同一个接口,表现出不同的行为"。

也就是说,同样的函数调用语句,在不同对象上会表现出不同的执行结果。

多态的作用

用于解决在继承关系中,基类与派生类存在同名函数时,调用哪个函数的问题

例如,当你用基类指针或引用操作派生类对象时, 系统会根据实际对象的类型来决定调用哪个函数。

多态的使用场景

  1. 使用 基类指针 指向 派生类对象

    cpp 复制代码
    Base* p = new Derived();
    p->func();
  2. 使用 基类引用 引用 派生类对象

    cpp 复制代码
    void call(Base& obj) { obj.func(); }
    Derived d;
    call(d);

二、形成多态的三个条件

条件 说明
① 有继承关系 基类 → 派生类
② 函数原型相同 返回类型、函数名、参数列表都相同
③ 基类函数前有 virtual 关键字 子类是否写 virtual 无影响,但父类必须有

注意:virtual 只在函数声明处出现,在定义实现时不能再次写。

三、虚函数与动态绑定

静态绑定 vs 动态绑定

  • 静态绑定(Static Binding): 在编译阶段确定调用哪个函数(如非虚函数、普通对象调用)。

  • 动态绑定(Dynamic Binding): 在运行时根据对象的真实类型决定调用哪个函数(即虚函数机制)。

示例:静态绑定与动态绑定对比

cpp 复制代码
#include <iostream>
using namespace std;

class Point {
public:
    Point(double i, double j) : x(i), y(j) {}
    double Area() const { return 0.0; }   // 非虚函数(静态绑定)
private:
    double x, y;
};

class Rectangle : public Point {
public:
    Rectangle(double i, double j, double w, double h) : Point(i, j), w(w), h(h) {}
    double Area() const { return w * h; } // 覆盖函数
private:
    double w, h;
};

void fun(Point &s) {
    cout << "Area=" << s.Area() << endl;
}

int main() {
    Rectangle rec(3.0, 5.2, 15.0, 25.0);
    fun(rec);
}

输出:

cpp 复制代码
Area=0

这是静态绑定,fun() 的参数类型是 Point&,所以调用的是 Point::Area()

改进:加上虚函数实现动态绑定

cpp 复制代码
#include <iostream>
using namespace std;

class Point {
public:
    Point(double i, double j) : x(i), y(j) {}
    virtual double Area() const { return 0.0; } // 虚函数
private:
    double x, y;
};

class Rectangle : public Point {
public:
    Rectangle(double i, double j, double w, double h) : Point(i, j), w(w), h(h) {}
    double Area() const override { return w * h; } // 重写(覆盖)
private:
    double w, h;
};

void fun(Point &s) {
    cout << "Area=" << s.Area() << endl;
}

int main() {
    Rectangle rec(3.0, 5.2, 15.0, 25.0);
    fun(rec);
}

输出:

cpp 复制代码
Area=375

调用了派生类的 Rectangle::Area()。 动态绑定通过虚函数表(vtable)在运行时确定实际调用的函数。

四、虚函数表(Virtual Table)机制解析

虚表(vtable)

  • 虚表是编译器为含虚函数的类自动生成的函数指针数组

  • 数组中的每个元素存放虚函数的入口地址;

  • 类中有虚函数 → 编译器自动添加一个隐藏的 虚表指针(vptr)

  • 通常 vptr 是类对象内存中的第一个成员。

五、虚析构函数

为什么需要虚析构函数?

当通过 基类指针删除派生类对象 时:

cpp 复制代码
Base* p = new Derived();
delete p; // 只会调用 Base::~Base()

如果基类析构函数不是虚函数,就会造成派生类资源泄漏

解决办法:

cpp 复制代码
virtual ~Base() { ... }

构造函数不能是虚函数,但析构函数可以(且经常需要)。

六、抽象类与纯虚函数

定义

带有至少一个 纯虚函数 的类称为 抽象类

cpp 复制代码
class Shape {
public:
    virtual double Area() = 0; // 纯虚函数
};
  • 抽象类不能创建对象;

  • 只能作为基类,被派生类继承并实现纯虚函数。

示例:抽象类实现多态

cpp 复制代码
#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw() = 0; // 纯虚函数
};

class Circle : public Shape {
public:
    void draw() override { cout << "Drawing Circle" << endl; }
};

class Rectangle : public Shape {
public:
    void draw() override { cout << "Drawing Rectangle" << endl; }
};

void render(Shape* s) {
    s->draw();
}

int main() {
    Circle c;
    Rectangle r;
    render(&c);
    render(&r);
}

输出:

cpp 复制代码
Drawing Circle
Drawing Rectangle

七、运算符重载与多态的联系

在 C++ 中,函数重载、运算符重载、虚函数 都体现了多态的思想:

  • 函数重载:编译期多态(静态绑定);

  • 虚函数:运行期多态(动态绑定);

  • 运算符重载:通过"同一符号 → 不同类型行为"实现多态。

示例:运算符重载实现复数加减

cpp 复制代码
#include <iostream>
using namespace std;

class Complex {
public:
    Complex(double r=0.0, double i=0.0) : real(r), imag(i) {}

    Complex operator+(Complex c2) {
        return Complex(real + c2.real, imag + c2.imag);
    }

    Complex operator-(Complex c2) {
        return Complex(real - c2.real, imag - c2.imag);
    }

    void display() const {
        cout << "(" << real << "," << imag << ")" << endl;
    }

private:
    double real, imag;
};

int main() {
    Complex c1(5, 4), c2(2, 10), c3;
    cout << "c1="; c1.display();
    cout << "c2="; c2.display();
    c3 = c1 - c2;
    cout << "c3=c1-c2="; c3.display();
    c3 = c1 + c2;
    cout << "c3=c1+c2="; c3.display();
}

输出:

cpp 复制代码
c1=(5,4)
c2=(2,10)
c3=c1-c2=(3,-6)
c3=c1+c2=(7,14)

运算符重载是"编译期多态"的体现。

八、总结与对比

多态的核心意义:

让"接口与实现分离",通过统一的接口实现灵活可扩展的运行行为。

三要素:

  1. 基于继承;

  2. 相同函数原型;

  3. virtual 关键字启用动态绑定。

相关推荐
Gerardisite2 小时前
如何在微信个人号开发中有效管理API接口?
java·开发语言·python·微信·php
Want5953 小时前
C/C++跳动的爱心①
c语言·开发语言·c++
lingggggaaaa3 小时前
免杀对抗——C2远控篇&C&C++&DLL注入&过内存核晶&镂空新增&白加黑链&签名程序劫持
c语言·c++·学习·安全·网络安全·免杀对抗
phdsky3 小时前
【设计模式】建造者模式
c++·设计模式·建造者模式
H_-H3 小时前
关于const应用与const中的c++陷阱
c++
coderxiaohan3 小时前
【C++】多态
开发语言·c++
gfdhy3 小时前
【c++】哈希算法深度解析:实现、核心作用与工业级应用
c语言·开发语言·c++·算法·密码学·哈希算法·哈希
Eiceblue4 小时前
通过 C# 将 HTML 转换为 RTF 富文本格式
开发语言·c#·html
故渊ZY4 小时前
Java 代理模式:从原理到实战的全方位解析
java·开发语言·架构
leon_zeng04 小时前
Qt Modern OpenGL 入门:从零开始绘制彩色图形
开发语言·qt·opengl