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. 构造函数

  • 规则

1.当基类存在默认构造函数时:如果基类有隐式或显式的无参构造函数(即默认构造函数),派生     类的构造函数初始化列表不需要显式调用基类构造函数。编译器会自动隐式调用基类的默认构造     函数。示例:

class Person {
public:Person() {} // 默认构造函数(可以隐式生成)
};class Student : public Person {
public:Student() {} // 隐式调用 Person::Person()
};

2.当基类没有默认构造函数时:如果基类的构造函数需要参数,且没有定义无参构造函数,则派生类必须在初始化列表中显示调用基类的某个构造函数,否则会编译报错。示例:

class Person {
public:Person(int x) {} // 没有默认构造函数
};class Student : public Person {
public:Student() : Person(42) {} // 必须显式调用基类构造函数
};
  • 原理:基类成员初始化顺序优先于派生类成员

 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.mzph.cn/web/69252.shtml

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

相关文章

已验证正常,Java输入字符串生成PDF文件

Java输入字符串生成PDF文件过程&#xff1a; 在Java开发中&#xff0c;如何将字符串转换为 PDF 是一个常见的需求。网上找了很多例子都无法生成&#xff0c;经过多次尝试&#xff0c;终于实现了&#xff0c;特此记录一下。 1、引入pom.xml 添加所需的依赖 <dependency>&…

Mac M1 Comfyui 使用MMAudio遇到的问题解决?

问题1: AssertionError: Torch not compiled with CUDA enabled&#xff1f; 解决办法&#xff1a;修改代码以 CPU 运行 第一步&#xff1a;找到 /ComfyUI/custom_nodes/ComfyUI-MMAudio/mmaudio/ext/autoencoder/vae.py文件中的下面这两行代码 self.data_mean nn.Buffer(t…

从 .NET Framework 升级到 .NET 8 后 SignalR 问题处理与解决方案

随着 .NET Framework 向 .NET 8 的迁移&#xff0c;许多开发者在使用 SignalR 时遇到了一些前后端连接、配置、调用等方面的问题。尤其是在处理 SignalR 实时通信功能时&#xff0c;升级后的一些兼容性问题可能导致应用程序无法正常工作。本文将介绍在从 .NET Framework 升级到…

2025.2.5——五、[网鼎杯 2020 青龙组]AreUSerialz 代码审计|反序列化

题目来源&#xff1a;BUUCTF [网鼎杯 2020 青龙组]AreUSerialz 目录 一、打开靶机&#xff0c;整理信息 二、解题思路 step 1&#xff1a;代码审计 step 2&#xff1a;开始解题 突破protected访问修饰符限制 三、小结 一、打开靶机&#xff0c;整理信息 直接得到一串ph…

Docker深度解析:安装各大环境

安装 Nginx 实现负载均衡&#xff1a; 挂载 nginx html 文件&#xff1a; 创建过载目录&#xff1a; mkdir -p /data/nginx/{conf,conf.d,html,logs} 注意&#xff1a;在挂载前需要对 conf/nginx.conf 文件进行编写 worker_processes 1;events {worker_connections 1024; …

docker启动报错code=exited, status=1/FAILURE——问题排查

问题 在某台centos7机器上&#xff0c;启动docker服务 sudo systemctl start docker报下列错误&#xff1a; ● docker.service - Docker Application Container EngineLoaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)Active: …

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

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

PostGIS:使用shp2pgsql、pgsql2shp、OGR2OGR函数进行数据导入、导出

数据导入与导出函数 数据库数据导入与导出可以通过多个函数完成&#xff0c;QGIS文档介绍了3个函数&#xff1a; shp2pgsql、pgsql2shp、OGR2OGR&#xff0c;分别用于shp导入数据库、数据库文件导出为shp、数据转换为多种数据格式。 &#xff08;1&#xff09;shp2pgsql 在l…

【AIGC魔童】DeepSeek v3推理部署:vLLM/SGLang/LMDeploy

【AIGC魔童】DeepSeek v3推理部署&#xff1a;vLLM/SGLang/LMDeploy &#xff08;1&#xff09;使用vLLM推理部署DeepSeek&#xff08;2&#xff09;使用SGLang推理部署DeepSeek&#xff08;3&#xff09;使用LMDeploy推理部署DeepSeek &#xff08;1&#xff09;使用vLLM推理部…

《AI “造脸术”:生成对抗网络打造超真实虚拟人脸》

在科技飞速发展的当下&#xff0c;人工智能的浪潮席卷而来&#xff0c;其中生成对抗网络&#xff08;GANs&#xff09;技术以其独特的魅力&#xff0c;成为了生成高度真实感虚拟人脸的强大引擎。无论是影视制作中虚拟角色的塑造&#xff0c;还是游戏领域中多样化角色形象的构建…

C语言的灵魂——指针(2)

前言&#xff1a;上期我们介绍了如何理解地址&#xff0c;内存&#xff0c;以及指针的一些基础知识和运算&#xff1b;这期我们来介绍一下const修饰指针&#xff0c;野指针&#xff0c;assert断言&#xff0c;指针的传址调用。 上一篇指针&#xff08;1&#xff09; 文章目录 一…

Android studio 创建aar包给Unity使用

1、aar 是什么&#xff1f; 和 Jar有什么区别 aar 和 jar包 都是压缩包&#xff0c;可以使用压缩软件打开 jar包 用于封装 Java 类及其相关资源 aar 文件是专门为 Android 平台设计的 &#xff0c;可以包含Android的专有内容&#xff0c;比如AndroidManifest.xml 文件 &#…

ASP.NET Core中Filter与Middleware的区别

中间件是ASP.NET Core这个基础提供的功能&#xff0c;而Filter是ASP.NET Core MVC中提供的功能。ASP.NET Core MVC是由MVC中间件提供的框架&#xff0c;而Filter属于MVC中间件提供的功能。 区别 中间件可以处理所有的请求&#xff0c;而Filter只能处理对控制器的请求&#x…

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

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

MySQL | Navicat安装教程

MySQL | Navicat安装教程 &#x1fa84;个人博客&#xff1a;https://vite.xingji.fun 简介 Navicat 是一款流行的 图形化数据库管理工具&#xff0c;由 PremiumSoft 公司开发&#xff0c;支持多种主流数据库系统&#xff08;如 MySQL、MariaDB、SQL Server、Oracle、Postgre…

硬件实现I2C案例(寄存器实现)

一、需求分析 二、硬件电路设计 本次案例需求与前面软件模拟案例一致&#xff0c;这里不再赘述&#xff0c;不清楚可参见下面文章&#xff1a;软件模拟I2C案例&#xff08;寄存器实现&#xff09;-CSDN博客 值得注意的是&#xff0c;前面是软件模拟I2C&#xff0c;所以并没有…

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

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

matlab simulink 汽车四分之一模型轮胎带阻尼

1、内容简介 略 matlab simulink121-汽车四分之一模型轮胎带阻尼 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

w196Spring Boot高校教师科研管理系统设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

【鸿蒙开发】第二十四章 AI - Core Speech Kit(基础语音服务)

目录 1 简介 1.1 场景介绍 1.2 约束与限制 2 文本转语音 2.1 场景介绍 2.2 约束与限制 2.3 开发步骤 2.4 设置播报策略 2.4.1 设置单词播报方式 2.4.2 设置数字播报策略 2.4.3 插入静音停顿 2.4.4 指定汉字发音 2.5 开发实例 3 语音识别 3.1 场景介绍 3.2 约束…