【深入解析C++多态:从原理到实践】

目录

1.类/对象的多态性***

运行时多态(动态多态)

怎样达到运行时多态?

程序是如何在运行时识别并调用子类实现的?

为什么要实现多态?

2.全局函数/同类中成员函数的多态性

编译时多态(静态多态)

虚函数使用规则

override

多态的实现原理

抽象类和接口类

抽象类

定义

特点

接口类

定义

特点


1.类/对象的多态性***

是考虑不同层次或同一层次函数的不同状态(意思就是同一个接口,不同的实现)

运行时多态(动态多态)

类成员函数在编译时,无法确定调用的位置,只有在运行时,依据函数名和参数表来确定你调用的位置

怎样达到运行时多态?
  1. 条件:继承+重写基类的虚函数+应用场景

    虚函数:virtual void hi(){...}

    vfptr 虚函数指针

  2. 应用场景:父类的引用/指针指向子类的对象

//继承+重写基类的虚函数+应用场景
class Person {
public:
    virtual void hi()
    {
        cout << "Person hi()" << endl;
    }
};
//1.继承
class Student :public Person {
    //2.重写基类的虚函数
    //override 关键字作用 确认此成员函数是否正确重写
    //重写虚函数的三同:返回类型、函数名、参数表
    virtual void hi() override
    {
        cout << "Student hi()" << endl;
    }
};
​
int main()
{
    /*Person p1;
    p1.hi();*/
​
    //3.运行时多态应用场景
    Person* p1 = new Student();
​
    p1->hi();
    return 0;
}

Student hi()

D:\C++\x64\Debug\C++.exe (进程 6440)已退出,代码为 0。 按任意键关闭此窗口. . .

class Person {
public:
    virtual void hi()
    {
        cout << "Person hi()" << endl;
    }
};
​
class Student :public Person {
    virtual void hi() override
    {
        cout << "Student hi()" << endl;
    }
};
​
class Teacher : public Person {
    virtual void hi() override
    {
        cout << "Teacher hi()" << endl;
    }
};
​
class Worker : public Person {
    virtual void hi() override
    {
        cout << "Worker hi()" << endl;
    }
};
​
void hello(Person& person)
{
    // 因为此处是Person类的引用
    // 同时, hi成员函数是虚函数,因此编译时无法确认调用的位置:编译器不知道你这个指针 / 引用,最终指向的是哪个子类对象
    // 只有在程序运行之后,调用此函数传入具体的对象时,才能确认调用位置
    person.hi();
}
​
int main()
{
    Person p1;
    Worker w1;
    Teacher t1;
    Student s1;
​
    //运行时多态体现
    hello(p1);
    hello(w1);
    hello(t1);
    hello(s1);
​
    return 0;
}

Person hi() Worker hi() Teacher hi() Student hi()

D:\C++\x64\Debug\C++.exe (进程 14088)已退出,代码为 0。 按任意键关闭此窗口. . .

程序是如何在运行时识别并调用子类实现的?

只要类里面有虚函数,对象就会自动带一个虚指针,父类对象里面有父类虚指针,子类对象里面有子类虚指针。只要子类重写了父类的虚函数,那么子类的虚表,会把父类虚函数的地址 “覆盖掉”。存放的就是子类函数的地址,调用会调用子类的

为什么要实现多态?

主要是为了统一接口,方便扩展,提高代码的可维护性

例子:

比如说如果没有多态,要为每个子类准备一个单独的函数。catspeak,dogspeak,pigspeak。有了多态之后,一个函数可以解决所有的子类,只写一个speak函数里面传子类的对象,函数规范简洁。

易于扩展主要就是比如说要写一个birdspeak,不用新写一个函数,只用创建一个类,继承animal,重写虚函数。

提高代码的可维护性,要修改的话改一处就好,重复代码太少了

2.全局函数/同类中成员函数的多态性

编译时多态(静态多态)

类成员函数在编译时,可以确定调用的位置

实现方式:函数重载、运算符重载、模板

可以根据参数/类型在编译时确定调用哪个版本

虚函数使用规则

1.派生类/子类重写虚函数时,必须三同(返回类型、函数名、参数表)

2.虚函数只能属于类(只修饰类的成员函数,外部全局函数是无法使用virtual关键字)

3.静态成员函数,内联函数,构造函数和拷贝构造函数,不能为虚函数

不能加virtual

  • 静态成员函数在子类中只能覆盖,重定义,不能加virtual,因为它没有this指针

  • 构造函数用于初始化成员,创建类时必须唯一确定

  • 拷贝构造函数也必须唯一确定

4.析构函数可以为虚函数(运行时多态必须用虚函数回收,因为实际回收的空间是子类的空间)

5.inline函数不能为虚函数

override

编译器会强制检查是否正确重写

class Person {
private:
    int pid;
    char* name;
public:
    Person(int pid, const char* name) :pid(pid) {
        this->name = new char[32] {0};
        strcpy(this->name, name);
    }
​
    virtual void hi() {
        cout << "Pid: " << pid << ", Name: " << name << endl;
    }
​
    virtual ~Person() {
        cout << "~Person()" << endl;
        delete[] name;
    }
};
​
class Student :public Person {
private:
    char* clsname;
public:
    Student(int pid, const char* name,const char* clsname) :Person(pid,name) {
        this->clsname = new char[32] {0};
        strcpy(this->clsname, clsname);
    }
​
    void hi()
    {
        Person::hi();
        cout << "clsName: " << clsname << endl;
    }
​
    ~Student() {
        cout << "~Student()" << endl;
        delete[] clsname;
    }
};
​
int main()
{
    Person* p = new Student(1001, "Lucy", "c++2502");
    p->hi();
    delete p;
    return 0;
}

Pid: 1001, Name: Lucy clsName: c++2502 ~Student() ~Person()

D:\C++\x64\Debug\C++.exe (进程 32516)已退出,代码为 0。 按任意键关闭此窗口. . .

5.基类的指针或引用,指向派生类的对象:

6.运行时多态,执行速度会稍慢些。

7.虚函数声明和定义分开时,只需要在声明上加virtual关键字。禁止在外部定义时加此virtual关键字。

多态的实现原理

运行时多态的原理

虚函数个数和虚函数表里面的个数不一样

image-20260413211800006

虚函数指针指向虚函数表,确定了实际虚函数的位置,虚函数表实际上是函数指针数组,里面存储虚函数地址,

抽象类和接口类

抽象类

定义

包含纯虚函数的类

纯虚函数 是没有函数体的,只声明接口,不实现:

virtual 返回类型 函数名(参数表)=0;

特点
  1. 不能实例化,不能创建对象(实例)

  2. 派生类未实现纯虚函数时,仍是抽象类(子类必须重写全部的纯虚函数,有一个没实现,他就是抽象类)

  3. 可以有构造函数、普通成员函数、成员变量

接口类

定义

类中所有的函数(除了构造函数)都是纯虚函数,且没有任何成员变量。

特点
  1. 没有任何成员变量

  2. 所有函数(除构造)都是纯虚函数

  3. 完全用来定义 “规范 / 协议 / 接口”

  4. 子类必须实现所有接口

  5. 专门用于约束行为、统一规范

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值