C++ 多态

server/2024/11/30 5:58:00/

C/C++总述:Study C/C++-CSDN博客 

目录

多态概念

多态分类

多态实现 

虚函数&虚函数表

虚函数的重写(覆盖)

多态的构成条件 

虚函数重写的两个特例

协变

析构

关键字final和override(C++11)

抽象类

纯虚函数

实现继承与接口继承


多态概念

对于同一个行为对于不同的对象,有不同的表现

eg:买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人 买票时是优先买票。 

多态分类

多态在 c++ 中分为 静态多态动态多态,也称 编译期多态 和 运行时多态 。

静态多态:是基于 函数重载 与 泛型编程 实现的。

动态多态:是基于虚函数实现的。

多态实现 

虚函数&虚函数表

  • 虚函数:即被 virtual 修饰的类成员函数称为虚函数。
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
  • 虚函数表本质是一个存虚函数指针 的 指针数组,一般情况这个数组最后面放了一个nullptr。
  • 虚函数表:虚函数表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚函数表中。
  • 一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表
  • 注意:对象的前四个字节就是虚表的地址,虚表存放在常量区(虚表是不能人为更改的)

虚函数的重写(覆盖)

  • 虚函数的重写(覆盖): 派生类中有一个跟基类完全相同的虚函数 (即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同,称子类的虚函数 重写 了基类的虚函数。

多态的构成条件 

  1. 必须通过 基类的指针 来 引用 调用虚函数
  2. 被调用的函数 必须是虚函数,且 派生类必须对基类的虚函数进行重写
class Person {               //多态条件2:被调用的函数 必须是虚函数
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person {
public:                      //多态条件2:派生类必须对基类的虚函数进行重写virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因
为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议
这样使用*//*void BuyTicket() { cout << "买票-半价" << endl; }*/
};void Func(Person& p)         //多态条件1:必须通过基类的指针来“引用”调用虚函数
{ p.BuyTicket(); 
}int main()
{Person ps;Student st;Func(ps);     //输出:买票-全价Func(st);     //输出:买票-半价return 0;
}

虚函数重写的两个特例

协变

派生类重写基类虚函数时 ,与基类虚函数 返回值类型不同 。即如下代码所示:【基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时】,称为协变。

class A{};
class B : public A {};class Person 
{
public:virtual A* f() {return new A;}
};class Student : public Person 
{
public:virtual B* f() {return new B;}
};
析构

如果 基类的析构函数为虚函数 ,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。 虽然函数名不相同~Person() ,~Student() ,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成 destructor

class Person 
{
public:                  //基类的析构函数为虚函数virtual ~Person() {cout << "~Person()" << endl;}
};class Student : public Person 
{
public:virtual ~Student() { cout << "~Student()" << endl; }
};
//只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}

关键字final和override(C++11)

final:修饰虚函数,使虚函数不能被覆盖。(加在父类中)
           final修饰类时,表示这个类不能被继承。

override:修饰虚函数,检测是否正确覆盖。(加在子类中)

class A
{
public:virtual void test()final   //使该虚函数不能覆盖,若被覆盖就报错{}
}class B: public A
{
public:virtual void test()override //检查虚函数是否正确覆盖,若未覆盖就报错{}
}

抽象类

含纯虚函数的类,称为抽象类

纯虚函数

  • 在虚函数后面加上 =0,则这个函数为纯虚函数。
  • 包含纯虚函数的类叫做抽象类(接口类),抽象类不能实例化出对象。
  • 子类继承抽象类后也不能实例化出对象,只有覆盖(重写)纯虚函数,子类才能实例化出对象。
  • 纯虚函数强制子类必须覆盖函数,更加体现出接口继承。
  • 纯虚函数不需要实现功能,只需要声明(重写时再实现功能)
class Car       // 抽象类
{
public:virtual void Drive() = 0;         //在虚函数的后面写上 =0,这个函数为 纯虚函数
};class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒适" << endl;  //只有 重写纯虚函数 ,派生类才能实例化出对象}
};class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};void Test()
{Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive();
}

实现继承与接口继承

  • 普通函数的继承是一种 实现继承 ,派生类继承了基类函数,可以使用函数,继承的是函数的实现。
  • 虚函数的继承是一种 接口继承 ,派生类继承的是基类虚函数的接口, 目的是为了重写,达成多态 ,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。

http://www.ppmy.cn/server/25733.html

相关文章

设备能源数据采集新篇章

在当今这个信息化、智能化的时代&#xff0c;设备能源数据的采集已经成为企业高效运营、绿色发展的重要基石。而今天&#xff0c;我们要向大家介绍的就是一款颠覆传统、引领未来的设备能源数据采集神器——HiWoo Box网关&#xff01; 一、HiWoo Box网关&#xff1a;一站式解决…

吐槽3家知名的AI智能体

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 我花了2天时间&#xff0c;把松松最近1年的爆款文案关键词情绪口头禅整理出来&#xff0c;4000多字的Prompt&#xff0c;都是一点点打出来的&#xff0c;再投喂到AI大模型里。使用的平台包括&#xff1a;通义千问、…

变电站自动化控制系统应用案例分析

变电站自动化控制系统介绍 变电站自动化控制系统用于大中型企业变电站项目&#xff0c;这类企业变压器多&#xff0c;日耗电量大。把多个变压器集中到一个电器平台上&#xff0c;集中管理分析&#xff0c;优化厂区用电管理&#xff0c;从而达到集中控制、集中分析、集中管理的…

【QEMU系统分析之实例篇(六)】

系列文章目录 第六章 QEMU系统仿真的机器创建分析实例 文章目录 系列文章目录第六章 QEMU系统仿真的机器创建分析实例 前言一、QEMU是什么&#xff1f;二、QEMU系统仿真的机器创建分析实例1.系统仿真的命令行参数2.目标机器创建过程3.static void pc_machine_initfn(Object *o…

【架构】后端项目如何分层及分层领域模型简化

文章目录 一. 如何分层1. 阿里规范2. 具体案例分析 二. 分层领域模型的转换1. 阿里规范2. 模型种类简化分析 三. 小结 本文描述后端项目中如何进行分层&#xff0c;以及分层领域模型简化 一. 如何分层 1. 阿里规范 阿里的编码规范中约束分层逻辑如下: 开放接口层&#xff1a…

【动态规划】Leetcode 32. 最长有效括号【困难】

最长有效括号 给你一个只包含 ‘(’ 和 ‘)’ 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 示例 2&#xff1a; 输入&#xff1a;s “)()())” 输出&#xff1a;4 解释&#xff1a;最长有效括号子串是 “()()” 解题思路 1…

一曲《少年中国说》令人情怀激荡

今天&#xff0c;作为四川籍人的本“人民体验官”&#xff0c;将充满自豪感地推广人民日报官方微博文化产品《看我中国少年&#xff01;川传学子热血合唱少年中国说》。 图片&#xff1a;来源“人民体验官”推广平台 人民微博说&#xff1a;“百年前&#xff0c;梁启超先生一篇…

【酱浦菌-爬虫技术细节】解决学术堂爬虫翻页(下一页)问题

首先我们通过css选择器获取页码信息&#xff0c;这里的css选择器&#xff0c;选择的是含有a标签的所有li标签&#xff0c;代码如下&#xff1a; li html_web.css(div.pd_c_xslb_left_fenye ul li>a) for li in li:li_url li.css(a::attr(href)).get()li_num li.css(a::t…