C++ 中的继承和多态

news2024/10/7 18:30:42

C++ 中的继承和多态

  • 一、继承
  • 二、函数重载、隐藏、覆盖、重写
    • 1.函数重载(Function Overload)
    • 2.函数隐藏(Function Hiding)
    • 3.函数重写与函数覆盖(Function Override)
  • 三、多态
  • 四、纯虚函数和抽象类
  • 五、多重继承的二义性(菱形继承)

在这里插入图片描述

一、继承

继承允许我们依据一个类来定义另一个类,这使得创建和维护一个应用程序变得更容易。这样做也达到了重用代码功能和提高执行效率的效果。

派生类的成员可以直接访问基类的保护成员(protected),但不能直接访问基类的私有成员(private)

继承分为公有继承、保护继承与私有继承,除了公有继承,剩下两个很少用到,三者区别如下:

在这里插入图片描述


二、函数重载、隐藏、覆盖、重写

1.函数重载(Function Overload)

C++规定在同一作用域中函数名相同但函数特征标(即参数个数、类型、顺序)不同时,构成函数重载

函数重载的注意事项:

  1. 返回值类型不能作为重载的标准。
  2. 参数是否为引用不能作为重载的标准,尽管某些时候能通过编译,但在调用时会产生二义性。
  3. 成员函数是否被static修饰也不能作为重载的标准,因为在通过实例化后的对象调用方法时无法区分是否要调用静态成员函数。
  4. 一个函数不能既作为重载函数,又作为有默认参数的函数,因为当调用函数时如果少写一个参数,系统无法判定是利用重载函数还是利用默认参数的函数,即 int func(int a)int func(int a = 0) 是不能在同一作用域中同时存在的。

这里还要特别注意一下const修饰函数或函数参数时的情况:

class A {
public:
    /**
     * 不管形参有没有const修饰实参都不会被修改,二者在调用时没有区别,因此不能构成重载
     */
    void func(int a);
    void func(const int a); // NO

    /**
     * 由底层const修饰的指针指向的实参不能被修改,二者在调用时存在区别,因此可以构成重载
     */
    void func_bot_p(int *a);
    void func_bot_p(const int *a); // YES

    /**
     * 不管有没有顶层const修饰,该指针指向的内容都可以被修改,二者在调用时没有区别,因此不能构成重载
     */
    void func_top_p(int *a);
    void func_top_p(int *const a); // NO

    /**
     * 在const修饰引用时实参不能被修改,二者在调用时存在区别,因此可以构成重载
     */
    void func_ref(int &a);
    void func_ref(const int &a); // YES

    /**
     * 由const修饰的成员函数只能由const对象调用,二者在调用时存在区别,因此可以构成重载
     */
    void func_ret(int a);
    void func_ret(int a) const; // YES
};

2.函数隐藏(Function Hiding)

不同作用域中定义的同名函数会构成函数隐藏(不要求函数返回值和函数参数类型相同)。

类成员函数会屏蔽全局函数派生类成员函数会屏蔽与其同名的基类成员函数(但如果该基类成员函数为虚函数,且函数返回值和特征标相同则构成函数重写)。

#include <iostream>

using namespace std;

void func() {
    cout << "global::func()" << endl;
}

class A {
public:
    /**
     * 隐藏了外部的func
     */
    void func() {
        cout << "A::func()" << endl;
    }

    void use_func() {
        func();
        ::func(); // 使用全局函数时要加作用域
    }
};

class B : public A {
public:
    /**
     * 隐藏了基类的func
     */
    void func() {
        cout << "B::func()" << endl;
    }

    void use_func() {
        func();
        A::func(); // 使用基类函数时要加作用域
    }
};

int main() {
    A a;
    B b;

    a.use_func();
    b.use_func();
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11
atreus@MacBook-Pro % ./main
A::func()
global::func()
B::func()
A::func()
atreus@MacBook-Pro % 

3.函数重写与函数覆盖(Function Override)

派生类中与基类同返回值类型、同名和同特征标虚函数重定义,构成虚函数覆盖,也叫虚函数重写

需要注意的是,在默认情况下,如果重新定义了继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针,这种新出现的特性叫做返回类型协变(covariance of return type)。

#include <iostream>

using namespace std;

class A {
public:
    void func() {
        cout << "A::func()" << endl;
    }

    virtual void func_v() {
        cout << "A::func_v()" << endl;
    }
};

class B : public A {
public:
    /* 函数隐藏 */
    void func() {
        cout << "B::func()" << endl;
    }

    /* 函数重载 */
    void func_v() override {
        cout << "B::func_v()" << endl;
    }
};

int main() {
    A *a = new B;

    a->func();
    a->func_v();

    delete a;
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11 
atreus@MacBook-Pro % ./main
A::func()
B::func_v()
atreus@MacBook-Pro % 

三、多态

多态是指一个方法同时具有多种形态,具体形态取决于调用该方法的具体对象。从实现的角度可以将多态分为编译时多态(主要通过函数重载和运算符重载实现)和运行时多态(主要通过虚函数和函数重写实现)。

对于运行时多态,其实现主要有三个前提:

  • 存在继承
  • 存在函数重写(覆盖)
  • 存在基类指针或者引用指向子类对象

运行时多态的实现要借助于动态绑定,动态绑定借助于虚函数实现,虚函数的限制如下:

  • 只有类的成员函数才能声明为虚函数。
  • 基类的析构函数可以是虚函数且通常声明为虚函数。
  • 构造函数不能为虚函数。
  • 内联函数不能是虚函数。
  • 静态成员函数不能是虚函数。

虚函数、虚函数表及虚函数实现多态的原理

其中,动态绑定是运行时绑定,通过地址实现,它是指基类的指针或引用有可能指向不同的派生类对象。对于非虚函数,执行时实际调用该函数的对象类型即为该指针或引用的静态类型。而对于虚函数,执行时实际调用该函数的对象类型为该指针或引用所指对象的实际类型


四、纯虚函数和抽象类

当类声明中包含纯虚函数(定义是末尾有 = 0 的虚函数)时,则不能创建该类的对象,这个类变为抽象类,C++中的抽象类类似于Java中的接口,抽象类必须至少包含一个纯虚函数

此外,对于抽象类还有以下注意事项:

  1. 抽象类只能用作其他类的基类,当然也可以作为另一个抽象类的基类
  2. 抽象类不能用来定义对象,不能实例化,也不能用作参数类型、函数返回类型或显式转换的类型
  3. 如果一个非抽象类从抽象类中派生,则其必须通过覆盖来实现所有的继承而来的抽象成员函数
#include <iostream>

/* 抽象类 */
class Car {
public:
    virtual void showName() = 0; // 纯虚函数
};

class Audi : public Car {
public:
    void showName() override { std::cout << "Audi" << std::endl; }
};

class Volvo : public Car {
public:
    void showName() override { std::cout << "Volvo" << std::endl; }
};

int main() {
    Audi audi;
    Volvo volvo;

    audi.showName(); // Audi
    volvo.showName(); // Volvo

    return 0;
}

五、多重继承的二义性(菱形继承)

菱形继承是指当类B和类C同时继承于基类A,类D同时继承于类B和类C,此时类A中的成员变量和成员函数继承到类D中就变成了两份,在D中调用A中的成员会导致二义性,同时一个变量分两份存储也存在内存空间浪费的问题。

在这里插入图片描述

通过虚基类虚继承机制,可以在多继承中只保留一份共同成员,从而解决了多继承导致的命名冲突数据冗余

在继承方式前面加上 virtual 关键字就是虚继承,如果不采用虚继承,在类D中使用类A中的m_a时则需要通过 B::m_aC::m_a 来指定具体使用哪个m_a。

#include <iostream>

using namespace std;

class A {
protected:
    int m_a = 0;
};

class B : virtual public A {
protected:
    int m_b = 1;
};

class C : virtual public A {
protected:
    int m_c = 2;
};

class D : public B, public C {
protected:
    int m_d = 3;

public:
    D() {
        cout << m_a << endl;
        cout << m_b << endl;
        cout << m_c << endl;
        cout << m_d << endl;
    }
};

int main() {
    D d;
    return 0;
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11
atreus@MacBook-Pro % ./main
0
1
2
3
atreus@MacBook-Pro % 

C++标准库中的iostream类就是一个虚继承的实际应用案例。iostream从istream和ostream直接继承而来,而istream和ostream又都继承自一个共同的名为base_ios的类,是典型的菱形继承。

在这里插入图片描述


参考:

https://cloud.tencent.com/developer/article/1177174
https://blog.csdn.net/weixin_39640298/article/details/88725073
http://c.biancheng.net/view/2280.html

在这里插入图片描述

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

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

相关文章

【Linux高级 I/O(1)】如何使用阻塞 I/O 与非阻塞 I/O?

本系列再次回到文件 I/O 相关话题的讨论&#xff0c;将会介绍文件 I/O 当中的一些高级用法&#xff0c;以应对不同应用场合的需求&#xff0c;主要包括&#xff1a;非阻塞 I/O、I/O 多路复用、异步 I/O、存储映射 I/O 以及文件锁。 非阻塞 I/O 关于“阻塞”一词前面已经给大家…

使用大语言模型集成工具 LangChain 创建自己的论文汇总和查询工具

Langchain可以帮助开发人员构建由大型语言模型(llm)支持的应用程序。它提供一个框架将LLM与其他数据源(如互联网或个人文件)连接起来。这允许开发人员将多个命令链接在一起&#xff0c;以创建更复杂的应用程序。包括最近比较火爆的AutoGPT等都是使用了Langchain框架进行开发的。…

【框架源码】Spring源码底层IOC容器加入对象的方式

1.Spring容器加入对象方式简介 使用XML配置文件 在XML配置文件中使用< bean >标签来定义Bean&#xff0c;通过ClassPathXmlApplicationContext等容器来加载并初始化Bean。 使用注解 使用Spring提供的注解&#xff0c;例如Component、Service、Controller、Repository等注…

一篇带你看懂什么是DesignOps(设计运营管理)

“好设计就是好生意”(Good design is good business) ----IBM创始人Thomas J. Watson早在20世纪50年代&#xff0c;IBM就开始关注好设计。IBM创始人Thomas J. Watson认为好的设计能很大程度提高产品的质量、功能和美观度&#xff0c;吸引更多的消费者和客户&#xff0c;增加销…

工程监测无线中继采集仪的寄存器(参数)汇总详解

工程监测无线中继采集仪的寄存器&#xff08;参数&#xff09;汇总详解 一、 寄存器&#xff08;参数&#xff09;汇总 无线中继采集发送仪有很多参数&#xff08;寄存器&#xff09;&#xff0c;对于一些简单的应用&#xff0c;用户无需关心这些参数&#xff0c;使用默认参数值…

【GPT-4理论篇-1】GPT-4核心技术探秘 | 京东云技术团队

前言 GPT-4已经发布有一段时间了&#xff0c;但是出于安全性等各种原因&#xff0c;OpenAI并没有公布GPT-4的技术细节和代码&#xff0c;而是仅仅给出了一个长达100页的技术报告[1]。 这个技术报告着重介绍了GPT-4的强大之处&#xff0c;仅仅给出了几个技术方向的概括&#x…

《汇编语言》- 读书笔记 - 实验

《汇编语言》- 读书笔记 - 实验 实验 1 查看 CPU 和内存&#xff0c;用机器指令和汇编指令编程1. 预备知识: Debug 的使用r 修改或显示寄存器的值d 查看内存数据e 编辑内存中指定地址的内容u 显示代码t 单步执行一条指令a 写入汇编指令g 执行到指定位置n 指定文件名L 加载文件W…

可视化大屏就是“面子工程”?那是你压根不了解大屏的真正功能

我经常收到这样的私信“企业投入可视化大屏有必要吗&#xff1f;有什么好处呢&#xff1f;”我想说在当前数据时代&#xff0c;企业提高自身竞争力多在这方面下功夫是有必要的。说“没用”的人&#xff0c;都是大屏做的失败的人群&#xff0c;其实“面子”作用确实有&#xff0…

HTTP的协议格式与Fiddler的应用

HTTP的协议格式与Fiddler的应用 &#x1f50e;HTTP协议是什么&#x1f50e;HTTP协议的工作过程&#x1f50e;HTTP的协议格式Fiddler 的应用抓包工具原理协议格式总结 &#x1f50e;结尾 &#x1f50e;HTTP协议是什么 HTTP(超文本传输协议)是一种应用非常广泛的应用层协议 对于…

栈和队列OJ题:LeetCode--20.有效的括号

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;今天给大家带来的是LeetCode--20.有效的括号 数 据 结 构 专 栏&#xff1a;数据结构 个 人 主 页 &#xff1a;stackY、 LeetCode 专 栏 &#xff1a;LeetCode刷题训练营 LeetCode--20.有效的括号&#xff1a;htt…

TypeError Object of type int64 is not JSON serializable

TypeError: Object of type int64 is not JSON serializable debug解析&#xff0c;在正常处理数据过程中&#xff0c;把模型预测结果&#xff0c;其他结构化数据转为str保存时&#xff0c;常常用到 json.dumps()&#xff0c;报错内容如标题所示。 原因&#xff1a;json.dump…

Vue图片处理解决方案,一篇就够了

文章目录 一、阅读须知二、解决的问题三、知识储备四、解决方案五、核心代码六、参考资料七、FileReader()对象八、Exif库使用九、Canvas画布十、动态创建标签并添加绑定事件十一、utils方法十二、vue中图片预览十三、工具网站 一、阅读须知 小帅爆肝整理的这篇文章&#xff0c…

华为OD机试真题 Java 实现【异常的打卡记录】【2023Q1 100分】

一、题目描述 考勤记录是分析和考核职工工作时间利用情况的原始依据&#xff0c;也是计算职工工资的原始依据。 为了正确地计算职工工资和监督工资基金使用情况&#xff0c;公司决定对员工的收集打卡记录进行异常排查。 如果出现以下两种情况&#xff0c;则认为打卡异常&…

持续引领 | 通付盾入选数说安全《2023年中国网络安全市场全景图》!

5月16日&#xff0c;数说安全正式发布《2023年中国网络安全市场全景图》&#xff08;以下简称全景图&#xff09;&#xff0c;这是自2018年开始&#xff0c;数说安全发布的第六版全景图。 其中&#xff0c;通付盾入选自动化攻击防护&#xff08;Anti-Bot&#xff09;、移动应用…

【软件工程】面向对象开发全面总结

文章目录 前言一、引言二、面向对象开发的含义三、面向对象开发的原则1. 封装原则2. 继承原则3. 多态原则4. 抽象原则5. 接口隔离原则6. 依赖倒置原则 四、面向对象开发的基本原则1. 单一职责原则2. 开放封闭原则3. 里氏替换原则 五、面向对象开发的方法1. 面向对象分析&#x…

阿里云服务器安装MySQL的具体步骤

1.首先打开阿里云&#xff0c;找到自己对于的公网ip 2.这里我使用MobaXterm的远程连接工具&#xff0c;这个工具还是很不错的耶&#xff0c;就是复制上图的公网ip地址 3.填写完成之后&#xff0c;会让你输入你阿里云服务端登入的密码&#xff0c;也就是你阿里云重置实例的密码&…

深度学习的十条调参经验

目录 法则一:调参、模型和数据 1、调参是锦上添花的事 2、模型 3、数据

富文本编辑器 kindeditor 使用整理

kindeditor富文本编辑器忘记什么时候添加到项目中了&#xff0c;最近做一个功能需要在原有编辑器上自定义添加一些固定内容&#xff0c;于是对着编辑器文档研究了一番&#xff0c;在这里做一个使用方式整理。 1.下载kindeditor 下载地址如下&#xff1a; https://github.com/…

视频平台。。。。 。

1.py 读取图片视频流 from flask import Flask from flask import render_template import os images_path_list[‘D:\imgs\’filename for filename in os.listdir(‘D:\imgs\’) if filename.endswith(‘.jpg’)] print(images_path_list) def return_img_stream(img_local_p…

城市内涝监测预警系统解决方案

一、方案背景 城市化进程的逐渐加快&#xff0c;城市内人口大量增多&#xff0c;交通运输压力增大&#xff0c;城市建设越来越密集&#xff0c;道路铺建面积越来越多&#xff0c;城市化的发展改变了城市区域的地表环境&#xff0c;致使城市雨水不能像农村或是古代雨水那样&…