C++——多态、抽象类和接口
目录
多态的基本概念
如何实现多态
在C++中,派生类对象可以被当作基类对象使用
编程示例
关键概念总结
抽象类
一、抽象类的定义
基本语法
二、抽象类的核心特性
1. 不能直接实例化
2. 派生类必须实现所有纯虚函数才能成为具体类
3. 可以包含普通成员函数和数据成员
编程示例
关键点说明
接口
接口的基本概念
多态的基本概念
想象一下,你有一个遥控器(这就像是一个基类的指针),这个遥控器可以控制不同的电子设备(这些设备就像是派生类)。无论是电视、音响还是灯光,遥控器上的“开/关”按钮(这个按钮就像是一个虚函数)都能控制它们,但具体的操作(打开电视、播放音乐、开灯)则取决于你指向的设备。
如何实现多态
1. 使用虚函数(Virtual Function):
我们在基类中定义一个虚函数,这个函数可以在任何派生类中被“重写”或者说“定制”。使用关键字 virtual 来声明。
2. 创建派生类并重写虚函数:
在派生类中,我们提供该虚函数的具体实现。这就像是告诉遥控器,“当你控制我的这个设备时,这个按钮应该这样工作”。
3. 通过基类的引用或指针调用虚函数:
当我们使用基类类型的指针或引用来调用虚函数时,实际调用的是对象的实际类型(派生类)中的函数版本。
当基类包含至少一个虚函数(通常是纯虚函数),派生类重写该函数后,在main函数中用基类指针指向派生类对象,并通过该指针调用虚函数时,实际调用的是派生类的实现,这种现象称为多态。
在C++中,派生类对象可以被当作基类对象使用
-
派生类对象可以直接赋值给基类指针/引用
-
但反过来不行(基类对象不能当作派生类对象)
编程示例
#include <iostream>
using namespace std;// 基类——遥控器(抽象概念)
class RemoteCon
{
public:// 虚函数:声明遥控器的"开启"功能,允许子类重写virtual void open() = 0; // 纯虚函数(=0表示没有默认实现)// 这样RemoteCon就成为抽象类,不能直接实例化
};// 派生类——电视遥控器,继承于基类遥控器
class TVRemoteCon : public RemoteCon
{
public:// 重写(override)基类的open函数void open() override{cout << "电视遥控器开启键被按下" << endl;}
};// 派生类——灯光遥控器,继承于基类遥控器
class LightRemoteCon : public RemoteCon
{
public:void open() override{cout << "灯光遥控器开启键被按下" << endl;}
};// 派生类——空调遥控器,继承于基类遥控器
class AirConditionerRemoteCon : public RemoteCon
{
public:void open() override{cout << "空调遥控器开启键被按下" << endl;}
};int main()
{// 第一部分:通过指针实现多态// 创建一个指向电视遥控器的基类指针RemoteCon* remoteCon = new TVRemoteCon(); remoteCon->open(); // 调用TVRemoteCon的open()// 创建一个指向灯光遥控器的基类指针RemoteCon* remoteCon2 = new LightRemoteCon();remoteCon2->open(); // 调用LightRemoteCon的open()// 第二部分:通过引用实现多态AirConditionerRemoteCon kongTiao; // 创建空调遥控器对象RemoteCon& remoteCon3 = kongTiao; // 创建基类引用绑定到派生类对象remoteCon3.open(); // 调用AirConditionerRemoteCon的open()// 释放动态分配的内存delete remoteCon;delete remoteCon2;return 0;
}
代码解释:
RemoteCon *remoteCon = new TVRemoteCon; // 创建一个指向电视的遥控器指针
remoteCon->open();
尽管 remoteCon 是 RemoteCon* 类型的指针,但它实际指向的是 TVRemoteCon 对象。在运行时,C++ 的多态机制会依据对象的实际类型来调用相应的 open 函数实现,所以这里调用的是 TVRemoteCon 类中的 open 函数。
关键概念总结
-
虚函数(virtual):允许函数在派生类中被重写
-
override关键字:明确表示重写基类虚函数
-
多态两种实现:
-
基类指针指向派生类对象
-
基类引用绑定派生类对象
-
-
运行时绑定:具体调用哪个函数在运行时决定
抽象类
一、抽象类的定义
抽象类是指包含至少一个纯虚函数的类,它不能被实例化,只能作为其他类的基类。
基本语法
class 抽象类名 {
public:virtual 返回类型 函数名(参数列表) = 0; // 纯虚函数// 其他成员...
};
二、抽象类的核心特性
1. 不能直接实例化
class Shape {
public:virtual double area() = 0;
};// Shape s; // 错误!不能创建抽象类对象
2. 派生类必须实现所有纯虚函数才能成为具体类
class Circle : public Shape {
public:double area() override { return 3.14*r*r; } // 必须实现
};
3. 可以包含普通成员函数和数据成员
class Animal {
public:virtual void speak() = 0;void eat() { cout << "Eating..." << endl; } // 普通成员函数
protected:int age; // 数据成员
};
编程示例
#include <iostream>using namespace std;// 这是一个抽象类,包含3个纯虚函数
// 因为有纯虚函数,所有这个类不能被实例化
class Teacher {
public:virtual void course() = 0; // 声明课程内容的纯虚函数virtual void startClass() = 0; // 开始上课的纯虚函数virtual void endClass() = 0; // 结束课程的纯虚函数
};// 具体派生类 EnglishTeacher
class EnglishTeacher : public Teacher {
public:void course() override {cout << "这节课是英语课" << endl;}void startClass() override {cout << "开始上英语课" << endl;}void endClass() override {cout << "英语课结束" << endl;}
};// 具体派生类 ProTeacher
class ProTeacher : public Teacher {
public:void course() override {cout << "这节课是C++" << endl;}void startClass() override {cout << "开始上C++" << endl;}void endClass() override {cout << "C++结束" << endl;}
};int main() {// 直接使用具体类EnglishTeacher t;t.course(); // 输出: 这节课是英语课t.startClass(); // 输出: 开始上英语课t.endClass(); // 输出: 英语课结束// 多态用法Teacher* t2 = new ProTeacher;t2->course(); // 输出: 这节课是C++t2->startClass(); // 输出: 开始上C++t2->endClass(); // 输出: C++结束delete t2; // 记得释放内存return 0;
}
关键点说明
-
多态的实现:
-
通过基类指针
Teacher*指向派生类对象ProTeacher -
调用虚函数时,实际执行的是派生类的实现
-
-
override关键字:
-
确保函数确实重写了基类的虚函数
-
如果签名不匹配,编译器会报错
-
-
抽象类的作用:
-
强制派生类实现特定接口
-
统一了不同种类教师的调用方式
-
-
内存管理:
-
示例中
new ProTeacher需要手动delete
-
接口
在C++中,接口是一种特殊的抽象类,它只包含纯虚函数而不包含任何数据成员或具体实现。接口用于定义行为规范,让不同的类可以遵循相同的接口标准。
接口的基本概念
在 C++ 中,虽然没有像其他编程语言(比如 Java 中的接口Interface)一样直接定义接口的关键字,但可以通过抽象类和纯虚函数的方式来实现接口的概念。
接口通常用于定义类应该实现的方法,但不提供具体实现。这样的实现方式允许多个类共享相同的接口,同时让每个类根据需要去实现这些接口。
一个类作为接口可以通过以下步骤来实现:
1. 定义抽象类:创建一个包含纯虚函数的抽象类,这些函数构成了接口的一部分。这些函数在抽象类中只有声明而没有具体的实现。
2. 派生类实现接口:派生类继承抽象类,并实现其中的纯虚函数,以具体实现接口定义的方法。
#include <iostream>using namespace std;class Person // 接口
{
public:virtual void eat() = 0; // 吃virtual void drink() = 0; // 喝virtual void haveFun() = 0; // 玩乐
};class Woman : public Person
{
public:void eat() override{cout << "麻辣鸡腿堡" << endl;}void drink() override{cout << "珍珠奶茶" << endl;}void haveFun() override{cout << "剧本杀" << endl;}
};class Man : public Person
{
public:void eat() override{cout << "板烧鸡腿堡" << endl;}void drink() override{cout << "可乐" << endl;}void haveFun() override{cout << "打桌球" << endl;}
};int main()
{Woman w;w.eat();w.drink();w.haveFun();Man m;m.eat();m.drink();m.haveFun();return 0;
}
Person 类是一个抽象基类,它包含了三个纯虚函数(eat(), drink(), haveFun()),这实际上起到了接口的作用。
Woman 和 Man 类继承自 Person 并实现了所有这些纯虚函数。
所以可以说这是一个接口的实现,但更准确的说法是:
-
这是一个使用 C++ 纯虚函数实现的接口设计模式
-
Person类扮演了接口的角色 -
Woman和Man是具体实现这个接口的类
在 C++ 中,这种设计常被称为"接口类"或"抽象基类",它强制派生类必须实现这些方法,这正是接口的核心特性。
相关文章:
C++——多态、抽象类和接口
目录 多态的基本概念 如何实现多态 在C中,派生类对象可以被当作基类对象使用 编程示例 关键概念总结 抽象类 一、抽象类的定义 基本语法 二、抽象类的核心特性 1. 不能直接实例化 2. 派生类必须实现所有纯虚函数才能成为具体类 3. 可以包含普通成员函数和…...
【Java面试笔记:基础】3.谈谈final、finally、 finalize有什么不同?
1. final、finally、finalize 的区别 final: 用途:用于修饰类、方法和变量。 修饰类:表示该类不能被继承。 final class ImmutableClass { // 此类无法被其他类继承 }修饰方法:表示该方法不能被子类重写。 class Parent {fin…...
基于 DeepSeek大模型 开发AI应用的理论和实战书籍推荐,涵盖基础理论、模型架构、实战技巧及对比分析,并附表格总结
以下是基于 DeepSeek大模型 开发AI应用的理论和实战书籍推荐,涵盖基础理论、模型架构、实战技巧及对比分析,并附表格总结: 1. 推荐书籍及内容说明 (1) 《深度学习》(Deep Learning) 作者:Ian Goodfellow…...
从数字化到智能化,百度 SRE 数智免疫系统的演进和实践
1. 为什么 SRE 需要数智免疫系统? 2022 年 10 月,在 Gartner 公布的 2023 年十大战略技术趋势中提到了「数字免疫系统」的概念,旨在通过结合数据驱动的一系列手段来提高系统的弹性和稳定性。 在过去 2 年的时间里,百度基于该…...
[Git] Git Stash 命令详解
1. Git Stash 的基本概念 Git Stash 是一个用于暂存当前工作目录中更改的命令。当你正在处理一个功能分支,但突然需要切换到另一个分支进行紧急修复或查看其他工作时,Git Stash 就显得非常有用。它允许你将当前工作目录中的更改保存起来,以便…...
ArcGIS及其组件抛出 -- “Sorry, this application cannot run under a Virtual Machine.“
产生背景: 使用的是“破解版本”或“被套壳过”的非官方 ArcGIS 版本 破解版本作者为了防止: 被研究破解方式 被自动化抓包/提权/逆向 被企业环境中部署多机使用 通常会加入**“虚拟化环境检测阻断运行”机制** 原因解释: 说明你当前运…...
Python项目调用Java数据接口实现CRUD操作
Django Python项目调用Java数据接口实现CRUD操作:接口设计与实现指南 引言 在现代软件架构中,系统间的数据交互变得越来越重要。Python和Java作为两种流行的编程语言,在企业级应用中常常需要实现跨语言的数据交互。本报告将详细介绍如何在D…...
进阶篇 第 5 篇:现代预测方法 - Prophet 与机器学习特征工程
进阶篇 第 5 篇:现代预测方法 - Prophet 与机器学习特征工程 (图片来源: ThisIsEngineering RAEng on Pexels) 在前几篇中,我们深入研究了经典的时间序列统计模型,如 ETS 和强大的 SARIMA 家族。它们在理论上成熟且应用广泛,但有…...
ubuntu 交叉编译 macOS 库, 使用 osxcross 搭建 docker 编译 OS X 库
1. ubuntu 交叉编译 macOS 库, 使用 osxcross 搭建 docker 编译 OS X 库 1. ubuntu 交叉编译 macOS 库, 使用 osxcross 搭建 docker 编译 OS X 库 1.1. 安装依赖1.2. 安装 osxcross 及 macOS SDK 1.2.1. 可能错误 1.3. 编译 cmake 类工程1.4. 编译 configure 类工程1.5. 单文件…...
JavaScript 中的单例模式
单例模式在 JavaScript 中是一种确保类只有一个实例,并提供全局访问点的方式。由于 JavaScript 的语言特性(如对象字面量、模块系统等),实现单例有多种方式。 常见实现方式 1. 对象字面量(最简单的单例) …...
深度学习基石:神经网络核心知识全解析(一)
神经网络核心知识全解析 一、神经网络概述 神经网络作为机器学习领域的关键算法,在现代生活中发挥着重要作用,广泛应用于图像识别、语音处理、智能推荐等诸多领域,深刻影响着人们的日常生活。它通过模拟人类大脑神经系统的结构和功能&#…...
java的反编译命令
1. javap -c: 显示方法中的字节码 2. javap -p: 显示所有访问级别,包括private 3. Javap -v: verbose模式,全信息,输出的内容包括: 类的访问标志(access_flags) 类名、父类、接口 …...
影刀填写输入框(web) 时出错: Can not convert Array to String
环境: 影刀5.26.24 Win10专业版 问题描述: [错误来源]行12: 填写输入框(web) 执行 填写输入框(web) 时出错: Can not convert Array to String. 解决方案: 1. 检查变量内容 在填写输入框之前,打印BT和NR变量的值ÿ…...
词语关系图谱模型
参数配置说明 sentences, # 分词后的语料(列表嵌套列表) vector_size100, # 每个词的向量维度 window5, # 词与上下文之间的最大距离(滑动窗口大小) min_count5, # 忽略出现次数小于5的…...
《C++ 模板:泛型编程的核心》
C模板详解 模板是C中实现泛型编程的重要特性,它允许你编写与数据类型无关的代码。模板可以分为函数模板和类模板两种。 1. 函数模板 函数模板允许你定义一个可以处理多种数据类型的函数。 基本语法 template <typename T> T functionName(T parameter1, T…...
HTTP的请求消息Request和响应消息Response
一:介绍 (1)定义 service方法里的两个参数 (2)过程 Request:获取请求数据 浏览器发送http请求数据(字符串),字符串被tomcat解析,解析后tomcat会将请求数据放入request对象 Response:…...
解决Python与Java交互乱码问题:从编码角度优化数据流
在现代软件开发中,跨语言系统的集成已经成为日常工作的一部分。特别是当Python和Java之间进行交互时,编码问题往往会成为导致数据传输错误、乱码以及难以调试的主要原因之一。 你是否曾遇到过这种情境:Python脚本通过标准输出返回了正确的数…...
DES、3DES、SM4 加密算法简介
1. DES(Data Encryption Standard) 设计时间:1975 年(IBM 开发,1977 年被 NIST 采纳为美国联邦标准)。 密钥长度:64 位(实际有效 56 位 8 位校验)。 分组长度…...
C++异步操作 - future async package_task promise
异步 异步编程是一种程序设计范式,允许任务在等待耗时操作(如I/O、网络请求)时暂停执行,转而处理其他任务,待操作完成后自动恢复。其核心目标是避免阻塞主线程,提升程序的并发性和响应速度…...
Feign 深度解析:Java 声明式 HTTP 客户端的终极指南
Feign 深度解析:Java 声明式 HTTP 客户端的终极指南 Feign 是由 Netflix 开源的 声明式 HTTP 客户端,后成为 Spring Cloud 生态的核心组件(现由 OpenFeign 维护)。它通过注解和接口定义简化了服务间 RESTful 通信,并…...
08前端项目----升序/降序
升序/降序 vue实现升序/降序服务器处理 vue实现升序/降序 用vue实现升序/降序,以及css绘制三角形 <div class"sui-navbar"><div class"navbar-inner filter"><ul class"sui-nav"><li class"active"&g…...
用Java实现简易区块链:从零开始的探索
📢 友情提示: 本文由银河易创AI(https://ai.eaigx.com)平台gpt-4o-mini模型辅助创作完成,旨在提供灵感参考与技术分享,文中关键数据、代码与结论建议通过官方渠道验证。 区块链技术作为近年来的热门话题&am…...
JavaScript 渲染内容爬取实践:Puppeteer 进阶技巧
进一步探讨如何使用 Puppeteer 进行动态网页爬取,特别是如何等待页面元素加载完成、处理无限滚动加载、单页应用的路由变化以及监听接口等常见场景。 一、等待页面元素加载完成 在爬取动态网页时,确保页面元素完全加载是获取完整数据的关键。Puppeteer…...
数据结构——栈以及相应的操作
栈(Stack) 在维基百科中是这样定义的: 堆栈(stack) 又称为栈或堆叠,是计算机科学中的一种抽象资料类型,只允许在有序的线性资料集合中的一端(称为堆栈顶端,top)进行加入数据(push)和…...
SVG 与 VSCode:高效设计与开发的完美结合
SVG 与 VSCode:高效设计与开发的完美结合 引言 随着互联网技术的飞速发展,网页设计已经成为了一个重要的领域。SVG(可缩放矢量图形)作为一种矢量图形格式,因其独特的优势,在网页设计中得到了广泛应用。而VSCode(Visual Studio Code)作为一款功能强大的代码编辑器,同…...
如何应对政策变化导致的项目风险
应对政策变化导致的项目风险,核心在于:加强政策研判机制、建立动态应对流程、构建合规应急预案、强化跨部门联动、提升项目柔性与调整能力。其中,加强政策研判机制 是所有防范工作中的“前哨哨兵”,可以让项目团队在政策风向转变之…...
ASP.Net Web Api如何更改URL
1.找到appsettings.json 修改如下: 主要为urls的修改填本机私有地址即可 {"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHosts": &q…...
在 Vue 3 中将拆分后的数组合并回原数组
接上文Vue 3 中按照某个字段将数组分成多个数组_vue3怎么进行数组对象--分割对象-CSDN博客 方法一:使用 flat() 方法 // 假设这是拆分后的多维数组 const splitArrays [[{id: 1, category: A}, {id: 3, category: A}],[{id: 2, category: B}, {id: 5, category: …...
【HTTPS协议原理】数据加密、如何防止中间人攻击、证书和签名、HTTPS完整工作流程
⭐️个人主页:小羊 ⭐️所属专栏:Linux网络 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 数据加密常见的加密方式数据摘要方案一:仅使用对称加密方案二:仅使用非对称加密方案三:双…...
Java中链表的深入了解及实现
一、链表 1.链表的概念 1.1链表是⼀种物理存储结构上⾮连续存储结构,数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的 实际中链表的结构⾮常多样,以下情况组合起来就有8种链表结构: 2.链表的实现 1.⽆头单向⾮循环链表实现 链表中的…...
