C++学习总结 - 多态

目录

概述

静态多态

动态多态

定义与实现

构成条件

虚函数

虚函数的特殊情况 

override   final

重载、覆盖、隐藏

抽象类

概念

接口继承与实现继承

多态原理

虚函数表

原理分析

单继承与多继承的虚函数

单继承的虚函数

多继承的虚函数

 菱形继承的虚函数


概述

  • 多态在具体应用场景下可以理解为在同一个软件下,不同账号去登录,从而可以实现不同的功能。
  • 多态的主要形式有两种,编译时的多态,即静态多态,另一种则是运行时的多态,即动态多态。

静态多态

函数重载实现,通过指定在同一个作用域中,定义多个函数名相同但是参数不同的函数

 运算符重载,允许用户定义或者改变C++内置运算符的行为,从而实现多态行为

 

动态多态

 继承与虚函数:继承允许派生类继承基类的属性和方法;虚函数则使得基类中函数可以被派生类重写。从而借助继承与虚函数配合,实现多态。

定义与实现

构成条件

静态多态构成条件总结

  • 函数重载:
    • 函数名相同:重载的函数必须具有相同的函数名
    • 参数列表不同:重载函数的参数类型或者参数数量必须不同
    • 返回类型无关:函数重载仅仅依赖于参数列表,与返回类型无关
  • 运算符重载
    • 内置运算符重定义:可以为类类型定义或重定义内置运算符行为
    • 至少一个操作数是用户定义的类型

动态多态构成条件

  • 继承关系:必须存在基类与派生类,拥有继承关系
  • 虚函数:基类中至少包含一个虚函数,使用virtual关键字声明
  • 函数重写:派生类必须重写基类中的虚函数
  • 基类指针或引用:通过基类指针或引用调用虚函数

虚函数

虚函数定义:通过virtual 关键字声明虚函数目,派生类可以重写该函数,来实现特定功能

重写虚函数:派生类中通过重新定义虚函数,来提供其具体实现,派生类中这个函数仍然是虚函数

借助基类指针或者引用调用虚函数,实际调用的是派生类的重写版本

纯虚函数与抽象类:因为纯虚函数没有具体实现的虚函数,所以通常使用=0表示。包含纯虚函数的类为抽象类,不能够实例化对象。 

虚函数表:每个包含虚函数的类,都有一张虚函数表,其中存储了这个类中所有虚函数的指针。在通过基类指针或者引用调用该函数的时候,程序会在运行时根据虚函数表找到实际调用的函数 

作用分析:

  • 动态绑定:运行的时候,根据对象的实际类型,调用正确的函数
  • 多态性:允许基类指针或者引用调用派生类的实现

虚函数的特殊情况 

协变返回类型:基类与派生类的返回类型不同,通常使用返回指针或者返回类型
(Derived :: clone 重写了Base:: clone 并返回一个Derived*类型的指针)

析构函数重写:确保在删除派类对象的时候能够正确调用派生类的析构函数,目的是保证资源正确释放避免内存泄漏 

override   final

 override:检查派生类虚函数是否发生重写基类的某个虚函数,如果没有发生重写则报错。目的是帮助编译器检查函数是否与基类中匹配。

final:指示类或虚函数不能被进一步继承或者重写,从而防止派生类进一步改变函数行为,确保其特定的实现不被修改

重载、覆盖、隐藏

重载

  • 同一作用域内,多个函数具有相同的名称,但是参数列表不同。编译器通过参数列表来区分这些函数。重载通常用于提高函数的灵活性和可读性。
  • 特点
    • 函数名相同
    • 参数列表不同
    • 与返回值无关

重写

  • 派生类中重新定义基类的虚函数,函数名、参数列表、返回类型必须与基类中的虚函数完全相同,重写则是用于多态
  • 特点
    • 函数名、参数、返回类型相同
    • 基类函数必须是虚函数
    • 派生类函数必须使用相同函数名、参数与返回类型

重定义(隐藏):派生类中定义了与基类同名但是参数、返回类型不同的函数,基类的同名函数在派生类中被隐藏。非虚函数的重定义不会导致运行时的多态,而是会隐藏基类的同名函数。 

  • 特点
    • 函数名相同
    • 参数列表或返回类型不同
    • 基类的同名函数被隐藏

抽象类

概念

抽象类是一种不能够被实例化的类,一般应用于定义接口,抽象类中至少有一个纯虚函数,该纯虚函数在基类中没有实现,必须在派生类中进行实现。

定义抽象类:虚函数声明后加上=0

接口继承与实现继承

 接口继承:派生类继承基类的函数声明,并在派生类中实现这些接口,抽象类通常是这些接口继承的基础。

实现继承:派生类继承基类的具体实现。派生类可以直接使用基类的实现,同时也可以重写基类的虚函数提供新的功能 

抽象类、接口继承、实现继承

  • 抽象类
    • 不能够实例化
    • 至少存在一个纯虚函数
    • 用于定义接口
  • 接口继承
    • 派生类继承基类的函数声明
    • 派生类必须实现基类的纯虚函数
  • 实现继承
    • 派生类继承基类的具体实现
    • 派生类可以直接使用基类的实现,也可以重写虚函数提供新的实现 

多态原理

派生类继承基类的虚函数表(数组指针,存储着函数地址),若派生类重写了基类函数,则更新虚函数表中的指针指向。

虚函数表

虚函数表是一个数组指针,其中每一个指针都指向类的一个虚函数。每个包含虚函数的类又都有一个虚函数表,该表包含这该类虚函数的指针。对象通过虚函数指针访问虚函数表。

虚函数表的实现机制

  • 类的虚函数表
    • 每个包含虚函数的类,在编译的时候都会生成一个虚函数表,虚函数表中包含着指向该类所有虚函数的指针
  • 对象的虚函数指针
    • 每个对象在内存中有一个隐藏的指针,指向所属类的虚函数表。当通过该对象调用虚函数的时候,程序会自动通过虚函数指针找到虚函数表,从而调用适当的函数

派生类虚表的生成

  • 派生类现将基类的虚表内容拷贝一份,放到派生类自己的虚表中
  • 如果派生类重写了基类中的某个虚函数,用派生类自己的虚函数地址覆盖基类的虚函数地址
  • 派生类自己新增加的虚函数按照其在派生类中声明次序增加到派生类虚表的最后 
  • 存储:虚表中存储的是虚函数指针,虚函数存放在内存的代码区中

原理分析

派生类中存在一个指向基类的指针,该指针指向其虚函数表,虚函数表中存储这函数地址,其中如果派生类发生了重写,则派生类中继承虚函数地址会相应发生改变,最后根据对象类型通过虚函数表完成动态绑定。

原理总结

  • 继承:派生类需要继承基类的接口和实现
  • 虚函数:基类中声明虚函数,派生类从而重写这些虚函数
  • 虚函数表:每个包含虚函数的类都有一个虚函数表,其中存储了该类所有的虚函数指针
  • 虚函数指针:每个对象包含一个指向虚函数表的指针,当通过该对象调用虚函数的时候,程序会通过虚函数指针找到虚函数表,从而调用对应的函数
  • 动态绑定:运行的时候,根据对象的实际类型,通过虚函数表实现动态绑定

 虚函数表地址验证

  • 创建对象地址与虚函数表地址验证
    • 虚函数表指针在创建的对象内存中
  • 虚函数表内容
    • 通过验证虚表中的地址验证虚函数表中存储的是虚函数指针

#include <iostream>

using namespace std;

class Base {
public:
    virtual void show() const {
        cout << "Base class show function" << endl;
    }
    virtual void display() const {
        cout << "Base class display function" << endl;
    }
};

class Derived : public Base {
public:
    void show() const override {
        cout << "Derived class show function" << endl;
    }
    void display() const override {
        cout << "Derived class display function" << endl;
    }
};

void printVTable(Base* obj) {
    // 假设虚函数指针位于对象的起始位置
    auto vptr = *(reinterpret_cast<void***>(obj));

    cout << "V-Table address: " << vptr << endl;

    // 假设有两个虚函数,打印虚函数表中的函数地址
    for (int i = 0; i < 2; ++i) {
        cout << "Function [" << i << "] address: " << vptr[i] << endl;
    }
}

int main() {
    Derived d;

    cout << "Derived object address: " << &d << endl;

    printVTable(&d);

    return 0;
}

 虚基表验证(继承)

  • 八字节:多态偏移量和虚继承的基类成员在子类当中的偏移量

#include <iostream>

using namespace std;

class Base {
public:
    virtual void show() const {
        cout << "Base class show function" << endl;
    }
};

class Derived1 : virtual public Base {
public:
    void show() const override {
        cout << "Derived1 class show function" << endl;
    }
};

class Derived2 : virtual public Base {
public:
    void show() const override {
        cout << "Derived2 class show function" << endl;
    }
};

class MostDerived : public Derived1, public Derived2 {
public:
    void show() const override {
        cout << "MostDerived class show function" << endl;
    }
};

void printVTable(Base* obj) {
    auto vptr = *(reinterpret_cast<void***>(obj));

    cout << "V-Table address: " << vptr << endl;

    // 假设有多个虚函数,打印虚函数表中的函数地址
    for (int i = 0; i < 3; ++i) {
        cout << "Function [" << i << "] address: " << vptr[i] << endl;
    }
}

void printVBaseTable(void* obj) {
    // 虚基表指针可能存储在对象的某个位置,具体取决于编译器实现
    auto vbptr = *(reinterpret_cast<void***>((char*)obj + sizeof(void*)));

    cout << "V-Base Table address: " << vbptr << endl;

    // 打印虚基表中的内容
    for (int i = 0; i < 2; ++i) {
        cout << "V-Base [" << i << "] address: " << vbptr[i] << endl;
    }
}

int main() {
    MostDerived md;

    cout << "MostDerived object address: " << &md << endl;

    // 打印Derived1部分的虚表
    Derived1* d1 = &md;
    cout << "Derived1 V-Table:" << endl;
    printVTable(d1);

    // 打印Derived2部分的虚表
    Derived2* d2 = &md;
    cout << "Derived2 V-Table:" << endl;
    printVTable(d2);

    // 打印虚基表
    cout << "V-Base Table:" << endl;
    printVBaseTable(d1);

    return 0;
}

单继承与多继承的虚函数

单继承的虚函数

单继承则是一个类继承自另外一个类,每个包含虚函数的类都有一个虚函数表,派生类的虚函数表指向自己的虚函数实现

 实验说明:Derived继承自Base,重写了show函数,所以Derived这个类覆盖了Base::show的地址

 

#include <iostream>
using namespace std;

class Base {
public:
    virtual void show() const {
        cout << "Base 类的 show 函数" << endl;
    }
};

class Derived : public Base {
public:
    void show() const override {
        cout << "Derived 类的 show 函数" << endl;
    }
};

void printVTable(void* obj) {
    auto vptr = *(reinterpret_cast<void***>(obj));
    cout << "虚函数表地址: " << vptr << endl;
    for (int i = 0; vptr[i] != nullptr; ++i) {
        cout << "函数 [" << i << "] 地址: " << vptr[i] << endl;
    }
}

int main() {
    Base b;
    Derived d;

    cout << "Base 对象地址: " << &b << endl;
    printVTable(&b);
    cout << endl;

    cout << "Derived 对象地址: " << &d << endl;
    printVTable(&d);

    return 0;
}

多继承的虚函数

一个类继承自多个基类,每个基类都会有自己的虚函数表,派生类包含多个虚函数表指针,分别指向各个基类的虚函数表

 实验解释:Derived类中有两个虚函数表指针,其分别是指向Base1 和 Base2的虚函数表。又因为在多继承中,Derive类重写了两个基类的函数,所以这两个虚表中包含的是Derived::show 和 Derived::display的地址

#include <iostream>
using namespace std;

class Base1 {
public:
    virtual void show() const {
        cout << "Base1 类的 show 函数" << endl;
    }
};

class Base2 {
public:
    virtual void display() const {
        cout << "Base2 类的 display 函数" << endl;
    }
};

class Derived : public Base1, public Base2 {
public:
    void show() const override {
        cout << "Derived 类的 show 函数" << endl;
    }
    void display() const override {
        cout << "Derived 类的 display 函数" << endl;
    }
};

void printVTable(void* obj) {
    auto vptr = *(reinterpret_cast<void***>(obj));
    cout << "虚函数表地址: " << vptr << endl;
    for (int i = 0; vptr[i] != nullptr; ++i) {
        cout << "函数 [" << i << "] 地址: " << vptr[i] << endl;
    }
}

int main() {
    Base1 b1;
    Base2 b2;
    Derived d;

    cout << "Base1 对象地址: " << &b1 << endl;
    printVTable(&b1);
    cout << endl;

    cout << "Base2 对象地址: " << &b2 << endl;
    printVTable(&b2);
    cout << endl;

    cout << "Derived 对象地址: " << &d << endl;
    cout << "Derived 继承自 Base1 的虚函数表:" << endl;
    Base1* pb1 = &d;
    printVTable(pb1);
    cout << endl;

    cout << "Derived 继承自 Base2 的虚函数表:" << endl;
    Base2* pb2 = &d;
    printVTable(pb2);

    return 0;
}

 菱形继承的虚函数

#include <iostream>
using namespace std;

class Base {
public:
    virtual void show() const {
        cout << "Base 类的 show 函数" << endl;
    }
};

class Derived1 : virtual public Base {
public:
    void show() const override {
        cout << "Derived1 类的 show 函数" << endl;
    }
};

class Derived2 : virtual public Base {
public:
    void show() const override {
        cout << "Derived2 类的 show 函数" << endl;
    }
};

class MostDerived : public Derived1, public Derived2 {
public:
    void show() const override {
        cout << "MostDerived 类的 show 函数" << endl;
    }
};

void printVTable(void* obj) {
    auto vptr = *(reinterpret_cast<void***>(obj));
    cout << "虚函数表地址: " << vptr << endl;
    for (int i = 0; vptr[i] != nullptr; ++i) {
        cout << "函数 [" << i << "] 地址: " << vptr[i] << endl;
    }
}

void printVBaseTable(void* obj) {
    auto vbptr = *(reinterpret_cast<void***>((char*)obj + sizeof(void*)));
    cout << "虚基表地址: " << vbptr << endl;
    for (int i = 0; vbptr[i] != nullptr; ++i) {
        cout << "虚基 [" << i << "] 地址: " << vbptr[i] << endl;
    }
}

int main() {
    Base b;
    Derived1 d1;
    Derived2 d2;
    MostDerived md;

    cout << "Base 对象地址: " << &b << endl;
    printVTable(&b);
    cout << endl;

    cout << "Derived1 对象地址: " << &d1 << endl;
    printVTable(&d1);
    cout << endl;

    cout << "Derived2 对象地址: " << &d2 << endl;
    printVTable(&d2);
    cout << endl;

    cout << "MostDerived 对象地址: " << &md << endl;

    cout << "MostDerived 继承自 Derived1 的虚函数表:" << endl;
    Derived1* pd1 = &md;
    printVTable(pd1);
    cout << endl;

    cout << "MostDerived 继承自 Derived2 的虚函数表:" << endl;
    Derived2* pd2 = &md;
    printVTable(pd2);
    cout << endl;

    cout << "虚基表:" << endl;
    printVBaseTable(pd1);

    return 0;
}

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值