C++中的多态

文章详细解释了多态的概念,即通过一个接口处理不同类型的对象。多态依赖于公有继承、基类指针或引用指向派生类对象以及派生类覆盖基类的虚函数。虚函数是实现多态的关键,它支持函数覆盖,并具有传递性。文章提供了代码示例展示如何使用多态,指出多态可以提高代码复用性,但可能导致派生类独特功能的丢失。此外,还强调了虚析构函数的重要性,以防止内存泄露,并介绍了动态类型绑定的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多态按照字面意思就是“多种状态”,可以简单地概括为“一个接口,多种状态”,程序在运行时才决定调用的函数内容,既同一个接口会根据传入的参数的个体差异,而采取不同的策略。

多态:同一调用语句在父类和子类间使用时具有不同的表现形式,可以使用同一段代码处理不同类型的对象,提高代码的复用性

多态的实现要有三个前提条件:

(1)要有公有继承

(2)基类引用/指针指向派生类对象

(3)派生类覆盖基类的成员函数

函数覆盖:

函数覆盖是通过虚函数实现的,用virtual关键字修饰成员函数,这样的成员函数就是虚函数,虚函数支持函数覆盖(不是函数隐藏)。函数覆盖是使用多态的前提

在C++11中可以通过override关键字来验证函数覆盖是否成功,只需要使用此关键字修饰派生类的新覆盖的函数即可。

虚函数的性质:

(1)虚函数具有传递性,当基类中某个成员函数设置为虚函数后,派生类中覆盖此函数的同名函数(函数名称相同、参数列表完全相同、返回值类型相关)——也自动变为虚函数

(2)只有成员函数和析构函数可以设置为虚函数

(3)如果成员函数声明与定义分离,只需要使用virtual修饰声明处即可

代码示例:
#include <iostream>

using namespace std;

class Animal
{
public:
    virtual void eat() // Qt Creator中虚函数使用斜体字
    {
        cout << "动物吃东西" << endl;
    }
};

class Dog:public Animal
{
public:
    void eat()
    {
        cout << "狗吃骨头" << endl;
    }
};

int main()
{
    Dog d;
    d.eat();
    d.Animal::eat(); // 调用基类被覆盖的函数

    return 0;
}

多态的基本使用:

多态的使用可以让一个继承家族的类使用同一个函数,极大提升了代码编程效率,但是使用多态后,派生类对象也会舍弃其独有的功能。

代码示例:
#include <iostream>

using namespace std;

class Animal
{
public:
    virtual void eat() // Qt Creator中虚函数使用斜体字
    {
        cout << "动物吃东西" << endl;
    }
};

class Dog:public Animal
{
public:
    void eat()
    {
        cout << "狗吃骨头" << endl;
    }

    void guard() // 狗类新增非虚函数
    {
        cout << "狗能看门" << endl;
    }
};

class Wolf:public Animal
{
public:
    void eat()
    {
        cout << "狼吃肉" << endl;
    }
};

class Husky:public Dog
{
public:
    void eat() override // 可以验证覆盖的有效性
    {
        cout << "哈士奇吃家具" << endl;
    }
};

// 基于引用的多态函数
void test_eat1(Animal& a)
{
    a.eat();
}

// 基于指针的多态函数
void test_eat2(Animal* a)
{
    a->eat();
}


int main()
{
    Animal a;
    Dog d;
    Wolf w;
    Husky h;
    test_eat1(a); // 动物吃东西
    test_eat1(d); // 狗吃骨头
    test_eat1(w); // 狼吃肉
    test_eat1(h); // 哈士奇吃家具

    Animal* ap = new Animal;
    Dog* dp = new Dog;
    Wolf* wp = new Wolf;
    Husky* hp = new Husky;
    test_eat2(ap); // 动物吃东西
    test_eat2(dp); // 狗吃骨头
    test_eat2(wp); // 狼吃肉
    test_eat2(hp); // 哈士奇吃家具

    // 多态的本质
    Animal& a0 = d;
    a0.eat(); // 狗吃骨头
//    a0.guard(); 使用多态时,不支持派生类独有的功能

    return 0;
}

在设计类时如果不写析构函数,编译器也会自动生成一个空的析构函数,如果这个作为根源基类(根源基类没有基类),可能会在多态时存在内存泄露的隐患,因此建议一个类的析构函数统一手写并设置虚析构函数,除非这个类不会作为其他类的基类

动态类型绑定:

多态之所以可以生效,是因为动态类型绑定的原理

当一个类有虚函数时,这个类会生成一个虚函数表,记录所有虚函数,同时此类的对象中有一个隐藏的成员变量,虚函数表指针,这个成员变量指向本类的虚函数表

当使用了基类的引用或指针指向派生类对象时,编译器会自动生成一段检查对象真正类型的代码,在程序运行时,通过对象的虚函数表指针去查找表记录的虚函数的调用地址

使用多态会降低程序的运行效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值