C++——多态、抽象类和接口

news2025/4/23 15:49:41

目录

多态的基本概念

如何实现多态

在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 函数。

关键概念总结

  1. 虚函数(virtual):允许函数在派生类中被重写

  2. override关键字:明确表示重写基类虚函数

  3. 多态两种实现

    • 基类指针指向派生类对象

    • 基类引用绑定派生类对象

  4. 运行时绑定:具体调用哪个函数在运行时决定

抽象类

一、抽象类的定义

抽象类是指包含至少一个纯虚函数的类,它不能被实例化,只能作为其他类的基类。

基本语法

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;
}

关键点说明

  1. 多态的实现

    • 通过基类指针Teacher*指向派生类对象ProTeacher

    • 调用虚函数时,实际执行的是派生类的实现

  2. override关键字

    • 确保函数确实重写了基类的虚函数

    • 如果签名不匹配,编译器会报错

  3. 抽象类的作用

    • 强制派生类实现特定接口

    • 统一了不同种类教师的调用方式

  4. 内存管理

    • 示例中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++ 中,这种设计常被称为"接口类"或"抽象基类",它强制派生类必须实现这些方法,这正是接口的核心特性。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2340878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于 DeepSeek大模型 开发AI应用的理论和实战书籍推荐,涵盖基础理论、模型架构、实战技巧及对比分析,并附表格总结

以下是基于 DeepSeek大模型 开发AI应用的理论和实战书籍推荐&#xff0c;涵盖基础理论、模型架构、实战技巧及对比分析&#xff0c;并附表格总结&#xff1a; 1. 推荐书籍及内容说明 (1) 《深度学习》&#xff08;Deep Learning&#xff09; 作者&#xff1a;Ian Goodfellow…

从数字化到智能化,百度 SRE 数智免疫系统的演进和实践

1. 为什么 SRE 需要数智免疫系统&#xff1f; 2022 年 10 月&#xff0c;在 Gartner 公布的 2023 年十大战略技术趋势中提到了「数字免疫系统」的概念&#xff0c;旨在通过结合数据驱动的一系列手段来提高系统的弹性和稳定性。 在过去 2 年的时间里&#xff0c;百度基于该…

ArcGIS及其组件抛出 -- “Sorry, this application cannot run under a Virtual Machine.“

产生背景&#xff1a; 使用的是“破解版本”或“被套壳过”的非官方 ArcGIS 版本 破解版本作者为了防止&#xff1a; 被研究破解方式 被自动化抓包/提权/逆向 被企业环境中部署多机使用 通常会加入**“虚拟化环境检测阻断运行”机制** 原因解释&#xff1a; 说明你当前运…

进阶篇 第 5 篇:现代预测方法 - Prophet 与机器学习特征工程

进阶篇 第 5 篇&#xff1a;现代预测方法 - Prophet 与机器学习特征工程 (图片来源: ThisIsEngineering RAEng on Pexels) 在前几篇中&#xff0c;我们深入研究了经典的时间序列统计模型&#xff0c;如 ETS 和强大的 SARIMA 家族。它们在理论上成熟且应用广泛&#xff0c;但有…

影刀填写输入框(web) 时出错: Can not convert Array to String

环境&#xff1a; 影刀5.26.24 Win10专业版 问题描述&#xff1a; [错误来源]行12: 填写输入框(web) 执行 填写输入框(web) 时出错: Can not convert Array to String. 解决方案&#xff1a; 1. 检查变量内容 在填写输入框之前&#xff0c;打印BT和NR变量的值&#xff…

词语关系图谱模型

参数配置说明 sentences, # 分词后的语料&#xff08;列表嵌套列表&#xff09; vector_size100, # 每个词的向量维度 window5, # 词与上下文之间的最大距离&#xff08;滑动窗口大小&#xff09; min_count5, # 忽略出现次数小于5的…

HTTP的请求消息Request和响应消息Response

一&#xff1a;介绍 &#xff08;1&#xff09;定义 service方法里的两个参数 &#xff08;2)过程 Request:获取请求数据 浏览器发送http请求数据&#xff08;字符串&#xff09;&#xff0c;字符串被tomcat解析&#xff0c;解析后tomcat会将请求数据放入request对象 Response:…

C++异步操作 - future async package_task promise

异步 异步编程是一种程序设计范式&#xff0c;​​允许任务在等待耗时操作&#xff08;如I/O、网络请求&#xff09;时暂停执行&#xff0c;转而处理其他任务&#xff0c;待操作完成后自动恢复​​。其核心目标是​​避免阻塞主线程​​&#xff0c;提升程序的并发性和响应速度…

数据结构——栈以及相应的操作

栈(Stack) 在维基百科中是这样定义的&#xff1a; 堆栈(stack) 又称为栈或堆叠&#xff0c;是计算机科学中的一种抽象资料类型&#xff0c;只允许在有序的线性资料集合中的一端&#xff08;称为堆栈顶端&#xff0c;top&#xff09;进行加入数据&#xff08;push&#xff09;和…

如何应对政策变化导致的项目风险

应对政策变化导致的项目风险&#xff0c;核心在于&#xff1a;加强政策研判机制、建立动态应对流程、构建合规应急预案、强化跨部门联动、提升项目柔性与调整能力。其中&#xff0c;加强政策研判机制 是所有防范工作中的“前哨哨兵”&#xff0c;可以让项目团队在政策风向转变之…

ASP.Net Web Api如何更改URL

1.找到appsettings.json 修改如下&#xff1a; 主要为urls的修改填本机私有地址即可 {"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHosts": &q…

【HTTPS协议原理】数据加密、如何防止中间人攻击、证书和签名、HTTPS完整工作流程

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;Linux网络 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 数据加密常见的加密方式数据摘要方案一&#xff1a;仅使用对称加密方案二&#xff1a;仅使用非对称加密方案三&#xff1a;双…

Java中链表的深入了解及实现

一、链表 1.链表的概念 1.1链表是⼀种物理存储结构上⾮连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的 实际中链表的结构⾮常多样&#xff0c;以下情况组合起来就有8种链表结构&#xff1a; 2.链表的实现 1.⽆头单向⾮循环链表实现 链表中的…

植物大战僵尸杂交版v3.6最新版本(附下载链接)

B站游戏作者潜艇伟伟迷于4月19日更新了植物大战僵尸杂交版3.6版本&#xff01;&#xff01;&#xff01;&#xff0c;有b站账户的记得要给作者三连关注一下呀&#xff01; 不多废话下载链接放上&#xff1a; 夸克网盘链接&#xff1a;&#xff1a;https://pan.quark.cn/s/1af9b…

【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f4da;欢迎订阅专栏…

背包问题模板

文章目录 01背包题意思路代码优化 完全背包题意思路代码优化 多重背包题意思路代码优化 分组背包题意思路代码 01背包 特点&#xff1a;每件物品最多只能用一次 01背包问题 题意 给出每件物品的体积v,价值w,求解能装入背包的的物品的最大价值&#xff0c;并且每件物品只能选一…

Sentinel源码—8.限流算法和设计模式总结二

大纲 1.关于限流的概述 2.高并发下的四大限流算法原理及实现 3.Sentinel使用的设计模式总结 3.Sentinel使用的设计模式总结 (1)责任链模式 (2)监听器模式 (3)适配器模式 (4)模版方法模式 (5)策略模式 (6)观察者模式 (1)责任链模式 一.责任链接口ProcessorSlot 二.责…

VulnHub-DarkHole_1靶机渗透教程

VulnHub-DarkHole_1靶机渗透教程 1.靶机部署 [Onepanda] Mik1ysomething 靶机下载&#xff1a;https://download.vulnhub.com/darkhole/DarkHole.zip 直接使用VMware打开就行 导入成功&#xff0c;打开虚拟机&#xff0c;到此虚拟机部署完成&#xff01; 注意&#xff1a…

边缘计算全透视:架构、应用与未来图景

边缘计算全透视&#xff1a;架构、应用与未来图景 一、产生背景二、本质三、特点&#xff08;一&#xff09;位置靠近数据源&#xff08;二&#xff09;分布式架构&#xff08;三&#xff09;实时性要求高 四、关键技术&#xff08;一&#xff09;硬件技术&#xff08;二&#…

MQ底层原理

RabbitMQ 概述 RabbitMQ 是⼀个开源的⾼性能、可扩展、消息中间件&#xff08;Message Broker&#xff09;&#xff0c;实现了 Advanced Message Queuing Protocol&#xff08;AMQP&#xff09;协议&#xff0c;可以帮助不同应⽤程序之间进⾏通信和数据交换。RabbitMQ 是由 E…