C++学习笔记(十九)——类之多态

发布于:2025-03-14 ⋅ 阅读:(21) ⋅ 点赞:(0)

一、多态

作用:
多态(Polymorphism) 是面向对象编程(OOP)的核心特性之一,它允许同一接口表现出不同的行为
多态可以分为:

  1. 编译时多态(静态多态): 通过函数重载运算符重载实现(在编译阶段确定调用哪个函数)。
  2. 运行时多态(动态多态): 通过虚函数(virtual)和基类指针/引用实现(在运行时确定调用哪个函数)。

特点:

  • 提高代码复用性,基类提供统一接口,子类可实现不同功能。
  • 扩展性强,无需修改基类即可新增功能。
  • 提高程序灵活性,如使用工厂模式(Factory Pattern) 动态创建对象。

二、静态多态 vs 动态多态

特性 静态多态(编译时) 动态多态(运行时)
实现方式 函数重载、运算符重载 继承 + 虚函数
调用决策时间 编译阶段 运行阶段
性能 (直接调用) 稍慢(需动态绑定)
适用场景 处理不同参数类型/个数 统一接口,灵活扩展

三、静态多态(编译时多态)

静态多态编译阶段确定调用哪个函数,通常通过以下方式实现:

  1. 函数重载(Function Overloading)
  2. 运算符重载(Operator Overloading)
  3. 模板(Template)

(1) 静态多态 - 函数重载

作用:
同一函数名,不同参数列表,编译时决定调用哪个版本,提高了代码的可读性和灵活性。

示例:

#include <iostream>
using namespace std;

class Math {
public:
    int add(int a, int b)
    {
        return a + b;
    }
    double add(double a, double b)
    {
        return a + b;
    }
};

int main() {
    Math obj;
    cout << obj.add(10, 20) << endl;   // 调用 int 版本
    cout << obj.add(2.5, 3.5) << endl; // 调用 double 版本

    system("pause");
    return 0;
}

(2) 静态多态 - 运算符重载

作用:
运算符重载(Operator Overloading) 允许我们为自定义类型(如classstruct定义运算符的行为,从而实现静态多态。

示例:

#include <iostream>
using namespace std;

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

    // 重载 +
    Complex operator+(const Complex& c)
    {
        return Complex(real + c.real, imag + c.imag);
    }

    void display()
    {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1(1, 2), c2(3, 4);
    Complex c3 = c1 + c2; // 静态多态,编译时决定调用哪个函数
    c3.display();

    system("pause");
    return 0;
}

注意:

  • 重载 + 运算符,使 c1 + c2 具备类似内置数据类型的加法运算行为。

(3) 静态多态 - 模板

作用:
模板(Template) 是实现静态多态的常见方式,适用于泛型编程(Generic Programming),允许编写与类型无关的代码,提高代码复用性和效率。

示例:

#include <iostream>
using namespace std;

// 泛型模板函数
template <typename T>
T add(T a, T b) 
{
    return a + b;
}

int main() {
    cout << add(10, 20) << endl;    // 调用 int 版本
    cout << add(2.5, 3.5) << endl;  // 调用 double 版本

    system("pause");
    return 0;
}

注意:

  • add(10, 20) —— 在编译阶段生成 int 版本
  • add(2.5, 3.5) —— 在编译阶段生成 double 版本
  • 编译时决定调用哪个版本,无额外运行时开销(静态绑定)。

四、 动态多态(运行时多态)

动态多态通过虚函数(virtual)和基类指针/引用实现。

  • 编译时无法确定调用哪个函数,只有在运行时才决定
  • 需要基类指针或引用指向子类对象,并重写(override)虚函数

(1) 基本虚函数

作用:
基本虚函数(Regular Virtual Function) 是在基类中声明为 virtual,但子类可以选择是否重写

  • 如果子类重写,则调用子类版本(运行时动态绑定)。
  • 如果子类不重写,则调用基类版本

示例:

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak()
    {
        cout << "动物在叫" << endl;
    }
};

class Dog : public Animal {
public:
    void speak() override
    {
        cout << "狗在叫:汪汪!" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() override
    {
        cout << "猫在叫:喵喵!" << endl;
    }
};

int main() {
    Animal* a;   // 基类指针
    Dog d;
    Cat c;

    a = &d;
    a->speak();  // 运行时确定调用 Dog::speak()

    a = &c;
    a->speak();  // 运行时确定调用 Cat::speak()

    system("pause");
    return 0;
}

注意:

  • speak()Animal 中定义为 virtual,使 a->speak() 在运行时决定调用哪个版本。
  • 没有 virtual 关键字时,调用的永远是 Animal::speak()(静态绑定)。
  • virtual 时,基类指针调用的是子类的 speak()(动态绑定)。

(2) 纯虚函数与抽象类

作用:

  • 纯虚函数= 0)表示该函数必须在子类中实现
  • 含有至少一个纯虚函数的类抽象类不能实例化

示例:

#include <iostream>
using namespace std;

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

class Circle : public Shape {
public:
    void draw() override
    {
        cout << "画一个圆" << endl;
    }
};

class Square : public Shape {
public:
    void draw() override
    {
        cout << "画一个正方形" << endl;
    }
};

int main() {
    Shape* s;  // 不能实例化抽象类
    Circle c;
    Square sq;

    s = &c;
    s->draw();  // 输出 "画一个圆"

    s = &sq;
    s->draw();  // 输出 "画一个正方形"

    system("pause");
    return 0;
}

注意:

  • Shape抽象类(包含纯虚函数 draw())。
  • 子类 CircleSquare 必须实现 draw(),否则也是抽象类。
  • 基类指针 s 运行时决定调用 Circle::draw()Square::draw()

(3) 多态 + 工厂模式

作用:
工厂模式(Factory Pattern)是创建型设计模式,用于封装对象创建逻辑,使得客户端代码无需关心具体类的实现,只需要调用工厂方法来获取对象

  • 封装对象创建逻辑,降低耦合。
  • 支持扩展,新增子类时无需修改已有代码(符合开闭原则)。
  • 结合多态,使基类指针指向不同的子类对象,实现动态绑定。

示例:

**#include <iostream>
using namespace std;

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

class Dog : public Animal {
public:
    void speak() override
    {
        cout << "狗:汪汪!" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() override
    {
        cout << "猫:喵喵!" << endl;
    }
};

// 工厂函数
Animal* createAnimal(int type)
{
    if (type == 1) 
        return new Dog();
    if (type == 2) 
        return new Cat();
    return nullptr;
}

int main() {
    Animal* a = createAnimal(1);
    a->speak();  // 动态创建狗
    delete a;

    a = createAnimal(2);
    a->speak();  // 动态创建猫
    delete a;

    system("pause");
    return 0;
}**

注意:

  • createAnimal() 返回不同的子类对象,基类指针 a 运行时决定调用哪个 speak()