从C++开始的编程生活(17)——多态

从C++开始的编程生活(17)——多态
前言

本系列文章承接C语言的学习,需要有C语言的基础才能学会哦~
第17篇主要讲的是有关于C++的多态
C++已经进入进阶,加油!!

目录

前言

多态

语法

实现多态的重要条件!!

重写(覆盖)的条件

协变(了解即可)

析构函数的重写

override和final关键字

纯虚函数和抽象类

抽象类

虚函数表

多态的原理

动态绑定和静态绑定

虚函数表原理


多态

多态分为静态多态动态多态

静态多态:传不同参数,编译出的函数就会不同(如函数重载、函数模板等)。
动态多态:同一个行为(函数),不同的对象完成的方式或者结果不一样(下文重点讲)。

语法

//函数重写Buyticket() class Peraon { public: virtual void BuyTicket() { cout << "买票——全价" << endl;}//① }; class Student : public Person{ public: virtual void BuyTicket() { cout << "买票——75折" << endl;}//② }; void Func(Person* ptr) { ptr->Buyticket(); } int main() { Person ps; Student st; //根据传入对象,调用指定的函数 Func(&ps);//调用① Func(&st);//调用② return 0; }

实现多态的重要条件!!

必须是父类的指针或引用去调用虚函数
        父类指针既可以指向父类也可以指向子类。
②被调用的函数必须是虚函数(关键字virtual在这里和虚继承的virtual没有关系)。
③子类必须对父类的虚函数进行重写

重写(覆盖)的条件

同名同参数同返回值虚函数函数体不同,即可构成重写。如果没有virtual修饰,就会被函数隐藏。

在满足多态的情况下,会调用子类重写的虚函数,但是函数声明遵循的是父类,因为继承了父类的整个函数签名(连virtual也继承了)。

class A { public: virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; } virtual void test() { func(); } }; class B : public A { public: void func(int val = 0) { std::cout << "B->" << val << std::endl; } }; int main() { B* p = new B; p->test(); return 0; }

观察上述代码,我们可以看到B的func满足了重写A的func的条件,函数声明的属性用的是父类的声明,但是调用的是子类的函数体,所以最后输出的结果是B->1,让val使用了父类的缺省值。

这里也告诉我们,在写多态的时候,父子类最好不要有不同的缺省值,不然就会有意外的事情发生。

协变(了解即可)

子类重写父类虚函数的时候,与父类虚函数的返回值类型不同。即父类虚函数返回父类对象的指针或者引用,子类虚函数返回子类对象的指针或者引用,这就叫做协变。(算是特殊情况,了解即可)。

析构函数的重写

子类的析构函数只要定义了,无论是否函数名是否相同,都可以与父类的析构函数构成重写编译器会把子类的析构看成重写,并且进行特殊处理

特殊处理:把父类和子类的函数名在编译时都换为destructor()

override和final关键字

override放在函数的括号后面修饰。被修饰的函数必须重写,不然就报错,可用于自检。
final也放在函数的括号后面修饰,被修饰的函数禁止重写,不然就报错,可用于自检。

纯虚函数和抽象类

(如果懂java可以类别为java的抽象接口)

纯虚函数:在虚函数的后面写上=0,那么这个函数就叫做纯虚函数,纯虚函数不需要定义实现,就算实现了也必须被重写,所以只用声明即可。

抽象类

包含纯虚函数的类叫做抽象类

抽象类不可以实例化出对象,如果其子类不对其纯虚函数进行重写,那么子类也是抽象类,也不能实例化出对象。

虚函数表

简称虚表,存储了抽象类中所有虚函数的指针。

抽象类存储时,除了存储成员变量外,还会存储一个隐藏的虚函数表指针,这个指针指向一个指针数组,存储了虚函数的指针。

class Base { public: virtual void Func1() { cout << "Func1()" << endl; } protected: int _b = 1; char _ch = 'x'; };

如上的抽象类,运用内存对齐的知识,在32位下,该类的大小为12字节,而不是8字节,因为需要先存储虚函数表指针。

                                        抽象类存储如上(_vftptr为虚函数表指针)

多态的原理

当抽象类被继承之后,其子类也会继承一个隐藏的成员——即虚函数表指针。但是指向的虚函数表不一样,子类的虚表指针指向的是自己的虚函数表。

因此,在进行多态调用的时候,会在对应的虚表中找到对应的虚函数进行调用

void Func(Person* ptr) { //因为指针类型是Person*,所以传入的子类指针,只能访问父类成员的切片 ptr->BuyTicket(); } int main() { Person ps; Student st; Func(&ps);//父类对象,调用父类对象的虚函数表中的函数 Func(&st);//子类对象,调用子类对象的虚函数表中的函数 }

如上,因为Func的参数为父类指针,

故:
子类调用的时候,只能访问到父类成员的切片,具有安全性
同时因为父子类存储的虚表不同,传入父类就调用父类的虚函数,传入子类就调用子类的虚函数,从而实现多态(调用了同一个函数Func却有不同的效果)。

总结:指针指向谁,调用时就实现谁。

动态绑定和静态绑定

静态绑定:对不满足多态条件的函数调用,在编译的时候就绑定好,编译时就已经确定了要调用的地址。

动态绑定:对满足多态条件的函数调用,在运行的时候才绑定,也就是运行时到指定对象的虚函数表中查询要调用的地址来调用。

虚函数表原理

①父类对象的虚函数表中存放着父类所有虚函数的地址。

子类由继承的父类和自己的成员组成,子类的虚表在继承的父类成员里,但是和父类的虚表不是相同的

子类重写了父类的虚函数后,就会用重写后的虚函数覆盖虚函数表里的函数。没重写就不覆盖。

④子类的虚函数表包括父类的虚函数地址、重写父类的虚函数地址、自己的虚函数地址(但是vs监视窗口会隐藏)。

⑤同类型对象使用的虚函数表是一样的。如Person p1和Person p2使用的是同一个虚表,不会再另外开辟空间。

⑥一般虚表这个数组最后会放一个0x00000000进行标记(vs有放,g++没放,具体看编译器定义)。

虚函数存放在代码段(也叫常量区)。在vs中,虚表也存放在代码段(虚表存放C++标准没要求)。

class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl; } virtual void func1() { cout << "func1" << endl; } void func2() { cout << "func2" << endl; } int _a1 = 1; int _a2 = 2; }; class Student : public Person { public: virtual void Buyticket() { cout << "买票-打折" << endl; } virtual void func1() { cout << "func1" << endl; } virtual void func3() { cout << "func2" << endl; } int _a3 = 3; int _a4 = 4; }; int main() { Person p; Student st; return 0; }

运行后,启动监视窗口

        可见父子类对象的虚函数表地址是不一样的,但是继承下来的_a1和_a2确实一样的。

补充:虽然函数名可以代表函数指针,但是取成员函数的地址要加取地址符号,这是语法规定,nowhy。

❤~~本文完结!!感谢观看!!接下来更精彩!!欢迎来我博客做客~~❤

Read more

将现有 REST API 转换为 MCP Server工具 -higress

将现有 REST API 转换为 MCP Server工具 -higress

Higress 是一款云原生 API 网关,集成了流量网关、微服务网关、安全网关和 AI 网关的功能。 它基于 Istio 和 Envoy 开发,支持使用 Go/Rust/JS 等语言编写 Wasm 插件。 提供了数十个通用插件和开箱即用的控制台。 Higress AI 网关支持多种 AI 服务提供商,如 OpenAI、DeepSeek、通义千问等,并具备令牌限流、消费者鉴权、WAF 防护、语义缓存等功能。 MCP Server 插件配置 higress 功能说明 * mcp-server 插件基于 Model Context Protocol (MCP),专为 AI 助手设计,

By Ne0inhk
MCP 工具速成:npx vs. uvx 全流程安装指南

MCP 工具速成:npx vs. uvx 全流程安装指南

在现代 AI 开发中,Model Context Protocol(MCP)允许通过外部进程扩展模型能力,而 npx(Node.js 生态)和 uvx(Python 生态)则是两种即装即用的客户端工具,帮助你快速下载并运行 MCP 服务器或工具包,无需全局安装。本文将从原理和对比入手,提供面向 Windows、macOS、Linux 的详细安装、验证及使用示例,确保你能在本地或 CI/CD 流程中无缝集成 MCP 服务器。 1. 工具简介 1.1 npx(Node.js/npm) npx 是 npm CLI(≥v5.2.0)

By Ne0inhk
解锁Dify与MySQL的深度融合:MCP魔法开启数据新旅程

解锁Dify与MySQL的深度融合:MCP魔法开启数据新旅程

文章目录 * 解锁Dify与MySQL的深度融合:MCP魔法开启数据新旅程 * 引言:技术融合的奇妙开篇 * 认识主角:Dify、MCP 与 MySQL * (一)Dify:大语言模型应用开发利器 * (二)MCP:连接的桥梁 * (三)MySQL:经典数据库 * 准备工作:搭建融合舞台 * (一)环境搭建 * (二)安装与配置 Dify * (三)安装与配置 MySQL * 关键步骤:Dify 与 MySQL 的牵手过程 * (一)安装必要插件 * (二)配置 MCP SSE * (三)创建 Dify 工作流 * (四)配置 Agent 策略 * (五)搭建MCP

By Ne0inhk
如何在Cursor中使用MCP服务

如何在Cursor中使用MCP服务

前言 随着AI编程助手的普及,越来越多开发者选择在Cursor等智能IDE中进行高效开发。Cursor不仅支持代码补全、智能搜索,还能通过MCP(Multi-Cloud Platform)服务,轻松调用如高德地图API、数据库等多种外部服务,实现数据采集、处理和自动化办公。 本文以“北京一日游自动化攻略”为例,详细讲解如何在 Cursor 中使用 MCP 服务,完成数据采集、数据库操作、文件生成和前端页面展示的全流程。 学习视频:cursor中使用MCP服务 一、什么是MCP服务? MCP(Multi-Cloud Platform)是Cursor内置的多云服务接口,支持调用地图、数据库、文件系统等多种API。通过MCP,开发者无需手动写HTTP请求或繁琐配置,只需在对话中描述需求,AI助手即可自动调用相关服务,极大提升开发效率。 二、环境准备 2.1 cursor Cursor重置机器码-解决Too many free trials. 2.

By Ne0inhk