C++----继承

一、继承的基本概念

本质:代码复用+类关系建模(是多态的基础)

class Person { /*...*/ };
class Student : public Person { /*...*/ }; // public继承
  • 派生类继承基类成员(数据+方法),可以通过监视窗口检验成员复用。

二、继承中的访问权限控制

访问权限变化表

基类成员访问限定符/继承方式public继承protected继承private继承
public成员->派生类public->派生类protected->派生类private
protected成员->派生类protected->派生类protected->派生类private
private成员不可见(但存在)不可见(但存在)不可见(但存在)

关键规则

  1. private成员:在派生类中始终不可访问(但存在于对象中)
  2. protected成员:专为继承设计,派生类可访问,外部不可访问。
  3. 访问权限计算:Min(成员在基类的权限,继承方式),权限等级:public>protected>private
  4. 默认继承方式:class默认private继承;struct默认public继承。
  5. 实际开发:优先使用public继承(占实际使用90%以上),慎用protected/private继承。

三、对象赋值转换规则

允许的操作

Student s;
Person p = s;         // 对象切片(调用拷贝构造)
Person& rp = s;       // 直接引用基类部分
Person* pp = &s;      // 直接指向基类部分

禁止的操作

// Person p;
// Student s = p;       // 错误!基类无法赋给派生类

关键注意

  • 对象切片:派生类->基类赋值时,丢失派生类特有成员。
  • 引用转换原理:派生类对象包含完整的基类子对象,无临时变量生成。

        类型系统对比:

  int i = 0;const double& rd = i;  // 需要const引用(临时变量具有常性)

四、继承中的作用域

核心规则

  1. 独立作用域:基类和派生类拥有独立的作用域。
  2. 隐藏/重定义:派生类成员与基类同名时,隐藏基类成员,包括成员变量和函数(无论参数是否一致)。
    class Base {
    public:void func(int) {}
    };class Derived : public Base {
    public:void func() { Base::func(42); // 必须显式指定作用域}
    };

重要细节

  • 函数隐藏与重载:派生类会隐藏基类所有同名函数(即使参数不同)
  • 访问被隐藏成员:使用作用域解析符
    Base::member
  • 设计建议:避免定义同名非虚函数

五、派生类默认成员函数

1. 构造函数

  • 规则:派生类构造函数必须通过基类构造函数初始化基类成员

  • 原理:基类成员初始化顺序优先于派生类成员

  • 代码示例

    class Student : public Person {
    public:Student(const char* name = "张三", int id = 0): Person(name)    // 必须显式调用基类构造函数, _id(id) {}
    protected:int _id;
    };

 2. 拷贝构造函数

  • 规则:需显式调用基类拷贝构造,完成基类部分的深拷贝

  • 代码示例

    Student(const Student& s): Person(s)         // 切片调用基类拷贝构造, _id(s._id) {}

3. 赋值运算符重载 

  • 规则:需要显式调用基类赋值运算符,处理自赋值情况

  • 代码示例

    Student& operator=(const Student& s) {if (this != &s) {Person::operator=(s);  // 显式调用基类赋值_id = s._id;}return *this;
    }

4. 析构函数 

  • 规则

    • 析构顺序:派生类->基类(自动调用基类析构)

    • 禁止显式调用基类析构函数

  • 代码示例

    ~Student() {// 自动调用Person::~Person()delete _ptr;  // 先清理派生类资源
    }

六、继承关系与友元 

  • 规则:基类友元不能访问派生类私有成员,需要额外声明

  • 代码示例

    class Student;  // 前向声明
    class Person {friend void Display(const Person&, const Student&);
    };
    class Student : public Person {friend void Display(const Person&, const Student&);
    };
    void Display(const Person& p, const Student& s) {cout << p._name << endl;    // 访问基类保护成员cout << s._stuNum << endl;  // 访问派生类保护成员
    }

七、静态成员与继承 

  • 特性

    • 基类静态成员被所有派生类共享

    • 继承的是访问权而非副本

  • 代码示例

    class Person
    {
    public :Person () {++ _count ;}
    protected :string _name ; // 姓名
    public :static int _count; // 统计人的个数。
    };
    int Person :: _count = 0;class Student : public Person
    {
    protected :int _stuNum ; // 学号
    };class Graduate : public Student
    {
    protected :string _seminarCourse ; // 研究科目
    };void TestPerson()
    {Student s1 ;Student s2 ;Student s3 ;Graduate s4 ;cout <<" 人数 :"<< Person ::_count << endl; //输出:人数 :4Student ::_count = 0;cout <<" 人数 :"<< Person ::_count << endl; //输出:人数 :0
    }

八、复杂继承模型 

  • 单继承:一个子类只有一个直接父类时称这个继承关系为单继承

  • 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

菱形继承:多继承的一种特殊情况

问题

数据冗余和二义性(从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。)

class Person
{
public :string _name ; // 姓名
};
class Student : public Person
{
protected :int _num ; //学号
};
class Teacher : public Person
{
protected :int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :string _majorCourse ; // 主修课程
};void Test ()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a ;    a._name = "peter";    // 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";
}

 

解决方案

虚拟继承(在Assistant的对象中Person成员只有一份。)

class Person
{
public :string _name ; // 姓名
};
class Student : virtual public Person
{
protected :int _num ; //学号
};
class Teacher : virtual public Person
{
protected :int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :string _majorCourse ; // 主修课程
};void Test ()
{Assistant a ;a._name = "peter";
}

虚拟继承解决数据冗余和二义性的原理

讲解

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。

class A { int _a; };
class B : public A { int _b; };
class C : public A { int _c; };
class D : public B, public C { int _d; };int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

 

下图是菱形继承的内存对象成员模型:这里可以看到数据冗余
下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下
面,这个A同时属于B和C,那么B和C如何去找到公共的A呢? 这里是通过了 B C 的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量
可以找到下面的A
  • 疑问1:为什么D中B和C部分要去找属于自己的A?

        那么大家看看当下面的赋值发生时,d是不是要去找出B/C成员中的A才能赋值过去?

  • 疑问2: 为什么要在B和C中存指针,而不直接存距离A的偏移量呢?

        ①存储内容多样性:

            - 多偏移量:菱形继承中,虚基表除存当                   前类到虚基类偏移量,在多重继承、复                   杂模板实例化等场景,还需存不同条件                   下访问虚基类的其他偏移量,用于正确                   定位成员。
            - 辅助信息:虚基表存储虚基类构造、析                   构函数指针等辅助信息,确保在对象构                   构造、析构及函数调用时正确操作。仅                   用偏移量无法存储这些信息,易致错误。

        ②支持复杂偏移关系:

            - 适应结构变化:使用虚基表指针,面对                   复杂继承结构变化(如 B、C 继承路径                     新增虚继承层次)时,虚基表可添加新                   偏移量或信息,指针仍能正确指向,保                   证虚基类成员访问。直接用偏移量,结                   构变化时需多处修改,维护扩展困难。

下面是上面的Person关系菱形虚拟继承的原理图:

九、继承与组合的选择

特征继承组合
关系性质"is-a"关系"has-a"关系
可见性白箱复用(了解实现细节)黑箱复用(接口隔离)
耦合度高耦合低耦合
多态支持支持不支持
代码复用方式垂直复用(扩展功能)水平复用(功能组合)

使用建议

  1. 优先使用对象组合

  2. 需要多态特性时使用继承

  3. 避免过度使用多继承

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】第二弹---数据库基础全解析:从概念到实践的深度探索

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】 目录 1. 数据库基础 1.1 什么是数据库 1.2 主流数据库 1.3 基本使用 1.3.1 MySQL安装 1.3.2 连接服务器 1.3.3 服务器…

深度学习:解码智能的“数字炼金术”

深度学习&#xff1a;解码智能的“数字炼金术” 1943年&#xff0c;当神经科学家沃伦麦卡洛克和数学家沃尔特皮茨在论文中首次提出人工神经元模型时&#xff0c;他们或许没有想到&#xff0c;这个简单的数学公式会在80年后掀起改变人类文明的技术革命。深度学习作为这场革命的…

无人机方位感知器官磁力传感器!

一、磁力计传感器的运作方式 基本原理&#xff1a; 磁力计传感器的工作原理基于磁场感应定律。当无人机处于地球磁场中时&#xff0c;磁力计可以测量地磁场的强度和方向。 磁力计通常采用三个互相垂直的磁阻传感器&#xff0c;每个轴向上的传感器检测在该方向上的地磁场强度…

基于SpringBoot养老院平台系统功能实现五

一、前言介绍&#xff1a; 1.1 项目摘要 随着全球人口老龄化的不断加剧&#xff0c;养老服务需求日益增长。特别是在中国&#xff0c;随着经济的快速发展和人民生活水平的提高&#xff0c;老年人口数量不断增加&#xff0c;对养老服务的质量和效率提出了更高的要求。传统的养…

2.7作业

手动将登录项目实现 //widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QtTextToSpeech/QTextToSpeech> #include <QPushButton> #include <QLabel> #include <QDebug> #include <QMovie> #include <QLineEd…

记录 | WPF基础学习Style局部和全局调用

目录 前言一、Style1.1 例子1.2 为样式起名字1.3 BasedOn 继承上一个样式 二、外部StyleStep1 创建资源字典BaseButtonStyle.xamlStep2 在资源字典中写入StyleStep3 App.xaml中写引用路径【全局】Step4 调用三、代码提供四、x:Key和x:Name区别 更新时间 前言 参考文章&#xff…

深度学习 Pytorch 神经网络的学习

本节将从梯度下降法向外拓展&#xff0c;介绍更常用的优化算法&#xff0c;实现神经网络的学习和迭代。在本节课结束将完整实现一个神经网络训练的全流程。 对于像神经网络这样的复杂模型&#xff0c;可能会有数百个 w w w的存在&#xff0c;同时如果我们使用的是像交叉熵这样…

vue项目如何设置默认页面

j​​​​​​​​​​​​​​​​​​​​​​​​​​​​创建一个新的vue项目并且将初始页面还原到空白项目开始 - 现实里的乌托邦 - 博客园 vue项目如何设置默认页面 • Worktile社区 创建一个vue项目并配置默认页面_vue如何设置初始展示的页面-CSDN博客 新建的vue项目…

DFX(Design for eXcellence)架构设计全解析:理论、实战、案例与面试指南*

一、什么是 DFX &#xff1f;为什么重要&#xff1f; DFX&#xff08;Design for eXcellence&#xff0c;卓越设计&#xff09;是一种面向产品全生命周期的设计理念&#xff0c;旨在确保产品在设计阶段就具备**良好的制造性&#xff08;DFM&#xff09;、可测试性&#xff08;…

基础篇05-图像直方图操作

本节将简要介绍Halcon中有关图像直方图操作的算子&#xff0c;重点介绍直方图获取和显示两类算子&#xff0c;以及直方图均衡化处理算子。 目录 1. 引言 2. 获取并显示直方图 2.1 获取&#xff08;灰度&#xff09;直方图 (1) gray_histo算子 (2) gray_histo_abs算子 (3…

Linux TCP 编程详解与实例

一、引言 在网络编程的领域中&#xff0c;TCP&#xff08;Transmission Control Protocol&#xff09;协议因其可靠的数据传输特性而被广泛应用。在 Linux 环境下&#xff0c;使用 C 或 C 进行 TCP 编程可以实现各种强大的网络应用。本文将深入探讨 Linux TCP 编程的各个方面&…

DeepSeek本地部署保姆级教程

由于DeepSeek近期遭受攻击&#xff0c;又加上用户访问量较大&#xff0c;导致总是服务不可用&#xff0c;让人十分窝火。有没有好的解决办法呢&#xff1f;答案是自己在电脑端部署一套&#xff0c;这样就不用和别人抢着用了。另外本地部署的好处还有保护隐私与减少延迟。 如果…

文件基础IO

理解"文件" 1-1 狭义理解 文件在磁盘里磁盘是永久性存储介质&#xff0c;因此文件在磁盘上的存储是永久性的磁盘是外设&#xff08;即是输出设备也是输入设备&#xff09;磁盘上的文件 本质是对文件的所有操作&#xff0c;都是对外设的输入和输出简称IO 1-2 广义理…

C32.【C++ Cont】静态实现双向链表及STL库的list

目录 1.知识回顾 2.静态实现演示图 3.静态实现代码 1.初始双向链表 2.头插 3.遍历链表 4.查找某个值 4.任意位置之后插入元素 5.任意位置之前插入元素 6.删除任意位置的元素 4.STL库的list 1.知识回顾 96.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删 97.【C…

退格法记单词(类似甘特图)

退格法记单词&#xff0c;根据记忆次数或熟练程度退格&#xff0c;以示区分&#xff0c;该方法用于短时高频大量记单词&#xff1a; explosion爆炸&#xff0c;激增 mosquito蚊子granary粮仓&#xff0c;谷仓 offhand漫不经心的 transient短暂的slob懒惰而邋遢的…

MySQL三大日志——binlog、redoLog、undoLog详解

日志是mysql数据库的重要组成部分&#xff0c;记录着数据库运行期间各种状态信息&#xff0c;能帮助我们进行很多容错及分析工作&#xff0c;其中有三大日志与我们这些开发者息息相关&#xff0c;本文将介绍binlog、redoLog、undoLog三种日志&#xff1a; 1. redoLog 1.1 为什么…

995. K连续位的最小翻转次数

目录 一、题目二、思路2.1 解题思路2.2 代码尝试2.3 疑难问题 三、解法代码逻辑回顾示例运行过程初始状态&#xff1a;遍历过程&#xff1a; 最终结果总结 四、收获4.1 心得4.2 举一反三 一、题目 二、思路 2.1 解题思路 就是滑动窗口一个一个遍历&#xff0c;遇到情况就翻转…

部署LLM模型到云端

文章目录 1 ECS 云服务器部署2 函数计算FC3 人工智能平台PAI-EAS4 大模型服务平台百炼压测实验结果显示,由于本地设备算力有限,本地部署的模型服务无法满足低延迟和高并发的需求。针对这类线上业务,可以考虑云端部署。 下面先来看看本地部署和云端部署的特点对比。 由上可…

【python】简单的flask做页面。一组字母组成的所有单词。这里的输入是一组字母,而输出是所有可能得字母组成的单词列表

目录结构如下&#xff1a; . ├── static │ ├── css │ │ └── styles.css │ └── js │ └── scripts.js ├── templates │ ├── base.html │ ├── case_converter.html │ ├── index.html │ └── word_finder.html ├── app.py ├── tree.py…

intra-mart实现简易登录页面笔记

一、前言 最近在学习intra-mart框架&#xff0c;在此总结下笔记。 intra-mart是一个前后端不分离的框架&#xff0c;开发时主要用的就是xml、html、js这几个文件&#xff1b; xml文件当做配置文件&#xff0c;html当做前端页面文件&#xff0c;js当做后端文件&#xff08;js里…