C/C++ - 类的继承机制

目录

类继承

继承的本质

继承的作用

继承的语法

继承的权限

继承的构造析构

继承构造函数

继承析构函数

继承的基类调用

继承的对象赋值

继承与友元关系

继承与静态成员

不能被继承的类

构造函数

语法特性

单继承

单继承的特点

多继承

多继承概念

多继承语法

多继承初始

菱形继承

菱形继承问题

解决菱形问题


  • 类继承

    • 继承的本质

      • 继承是面向对象编程中的一个重要概念,它允许一个类(称为派生类或子类)从另一个类(称为基类或父类)继承属性和行为。
      • 派生类可以访问基类的非私有成员,并且可以添加自己特有的成员和方法。
    • 继承的作用

      • 代码重用:继承允许派生类从基类继承属性和行为,避免了重复编写相同的代码。
      • 层次结构:通过继承,可以创建类的层次结构,体现出不同类之间的关系。
      • 多态性:继承是实现多态性的基础。基类指针或引用可以指向派生类的对象,实现基于继承的多态调用。
    • 继承的语法

      class DerivedClass : [访问修饰符] 基类名  //访问修饰符可以是public、protected或private,用于指定从基类继承的成员在派生类中的访问权限。
      {// 派生类的成员和方法
      };//父类(基类)
      class Person
      {
      public:char* m_Name;		//姓名int	  m_Age;		//年龄char* m_Class;		//课程
      };//子类(派生类)
      class Teacher : public Person
      {
      public:char* m_Work;		//时间
      };//子类(派生类)
      class Student : public Person
      {
      public:char* m_Number;		//编号
      };
      
    • 继承的权限

      • ​public​​​​继承:基类的public​​​​成员在派生类中仍为public​​​​,基类的protected​​​​成员在派生类中仍为protected​​​​,但对派生类的对象来说,基类的private​​​​成员不可访问。
      • ​protected​​​​继承:基类的public​​​​和protected​​​​成员在派生类中都变为protected​​​​,基类的private​​​​成员不可访问。
      • ​private​​​​继承:基类的public​​​​和protected​​​​成员在派生类中都变为private​​​​,基类的private​​​​成员不可访问。
      • #include <iostream>// 基类
        class Base 
        {
        public:int publicVar;void publicMethod() {std::cout << "Base::publicMethod()" << std::endl;}protected:int protectedVar;void protectedMethod() {std::cout << "Base::protectedMethod()" << std::endl;}private:int privateVar;void privateMethod() {std::cout << "Base::privateMethod()" << std::endl;}
        };// 派生类 - public继承
        class PublicDerived : public Base 
        {
        public:void accessBaseMembers() {publicVar = 1;              // 可以访问public成员publicMethod();             // 可以访问public方法protectedVar = 2;           // 可以访问protected成员protectedMethod();          // 可以访问protected方法// privateVar = 3;           // 错误,不能访问private成员// privateMethod();          // 错误,不能访问private方法}
        };// 派生类 - protected继承
        class ProtectedDerived : protected Base 
        {
        public:void accessBaseMembers() {publicVar = 1;              // 可以访问public成员publicMethod();             // 可以访问public方法protectedVar = 2;           // 可以访问protected成员protectedMethod();          // 可以访问protected方法// privateVar = 3;           // 错误,不能访问private成员// privateMethod();          // 错误,不能访问private方法}
        };// 派生类 - private继承
        class PrivateDerived : private Base 
        {
        public:void accessBaseMembers() {publicVar = 1;              // 可以访问public成员publicMethod();             // 可以访问public方法protectedVar = 2;           // 可以访问protected成员protectedMethod();          // 可以访问protected方法// privateVar = 3;           // 错误,不能访问private成员// privateMethod();          // 错误,不能访问private方法}
        };int main() 
        {PublicDerived publicDerived;publicDerived.publicVar = 1;            // 可以访问public成员publicDerived.publicMethod();           // 可以访问public方法ProtectedDerived protectedDerived;// protectedDerived.publicVar = 1;      // 错误,不能访问public成员// protectedDerived.publicMethod();     // 错误,不能访问public方法PrivateDerived privateDerived;// privateDerived.publicVar = 1;        // 错误,不能访问public成员// privateDerived.publicMethod();
        
    • 继承的构造析构

      • 继承构造函数

        • 如果派生类定义了自己的构造函数,但没有使用初始化列表调用基类的构造函数,则基类的默认构造函数会被隐式调用。
        • 如果派生类定义了自己的构造函数,并使用初始化列表调用了基类的特定构造函数,则基类的该构造函数会被显式调用。
      • 继承析构函数

        • 派生类会自动继承基类的析构函数,无需显式定义。在派生类对象销毁时,会先调用派生类的析构函数,然后自动调用基类的析构函数。
      • 实例代码

        #include <iostream>class Base
        {
        public:Base(){std::cout << "Base默认构造函数" << std::endl;}Base(int nNum){m_Base = nNum;std::cout << "Base有参构造函数" << std::endl;}~Base(){std::cout << "Base析构函数调用" << std::endl;}int m_Base;
        };class Derived : public Base
        {
        public:int m_Derived;Derived(){std::cout << "Derived默认构造函数" << std::endl;}Derived(int nNum) : Base(5){m_Derived = nNum;std::cout << "Derived有参构造函数" << std::endl;}~Derived(){std::cout << "Derived析构函数调用" << std::endl;}};int main()
        {{Derived der(5);}return 0;
        }
        
    • 继承的基类调用

      • 使用作用域解析运算符(::​​​​)来调用基类的成员函数。

        class DerivedClass : public BaseClass {
        public:void someFunction() {BaseClass::baseFunction();  // 调用基类的成员函数}
        };
        
      • 在派生类中重新定义同名的成员函数,覆盖基类的成员函数。

        class DerivedClass : public BaseClass {
        public:void someFunction() {// 派生类的特定实现}
        };
        
      • 使用作用域解析运算符(::​​​​)来访问基类的成员变量。

        class DerivedClass : public BaseClass {
        public:void someFunction() {int value = BaseClass::baseVariable;  // 访问基类的成员变量}
        };
        
      • 重新定义成员变量覆盖基类的成员变量的语法

        class DerivedClass : public BaseClass {
        public:int derivedVariable;  // 重新定义的成员变量,覆盖基类的成员变量
        };
        
      • 在派生类的成员函数中,可以使用作用域解析运算符(::​​​​)来访问基类的成员函数和成员变量。

      • 派生类也可以重新定义同名的成员函数和成员变量,覆盖基类的成员函数和成员变量。

    • 继承的对象赋值

      • 基类对象不能直接赋值给派生类对象

      • 派生类对象可以赋值给基类的对象(PTR / REF ) - 切片

        class Person
        {
        protected:string _name; // 姓名string _sex; // 性别int _age; // 年龄
        };
        class Student : public Person
        {
        public:int _num; // 学号
        };
        void Test()
        {Student sobj;Person pobj = sobj;Person* pp = &sobj;Person& rp = sobj;
        }
        
    • 继承与友元关系

      • 友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

        class Student;
        class Person
        {
        public:friend void Display(const Person& p, const Student& s);
        protected:string _name; // 姓名
        };class Student : public Person
        {
        protected:int _stuNum; // 学号
        };
        void Display(const Person& p, const Student& s)
        {cout << p._name << endl;cout << s._stuNum << endl;
        }
        void main()
        {Person p;Student s;Display(p, s);
        }
        
    • 继承与静态成员

      • #include <iostream>class Person
        {
        public:static int m_static;int m_public;int m_protected;int m_private;
        };int Person::m_static = 0;class Student : public Person
        {
        public:int m_public;int m_Student;
        };int main()
        {std::cout << sizeof(Person) << std::endl;std::cout << sizeof(Student) << std::endl;Student::Person::m_static = 2;Student s;s.Person::m_public = 1;s.m_protected = 2;s.m_private = 3;s.m_public = 4;s.m_Student = 5;return 0;
        }
        
    • 不能被继承的类

      • 构造函数

        • 将父类的构造函数私有化(private),这样在子类中是不可见的。当子类对象实例化时,会无法调用构造函数
      • 语法特性

        • C++11中,增添了final关键字,可以将当前类设置为不可被继承,无论是否实例化,一旦有继承它的会立即报错
      • 代码示例

        #include <iostream>class Person final //final声音为不可被继承
        {
        public://删除基类默认构造函数//Person() = delete;private://基类默认函数设置私有//Person(){}
        };class Student : public Person
        {
        public:};int main()
        {Student s;return 0;
        }
        
  • 单继承

    • 单继承的特点

      #include <iostream>// 基类
      class BaseClass {
      public:void display() {std::cout << "BaseClass::display()" << std::endl;}
      };// 派生类
      class DerivedClass : public BaseClass {
      public:void displayDerived() {display();  // 调用基类的display()函数std::cout << "DerivedClass::displayDerived()" << std::endl;}
      };int main() {DerivedClass derivedObj;derivedObj.displayDerived();return 0;
      }
      
      • 派生类只能从一个基类中继承成员函数和成员变量。
      • 派生类可以访问和重写基类的成员函数和成员变量。
  • 多继承

    • 多继承概念

      • 多继承允许一个派生类从多个基类中继承成员和方法。
      • 派生类将同时拥有每个基类的成员和方法。
    • 多继承语法

      • class Derived : access-specifier Base1, access-specifier Base2, ..., access-specifier BaseN {// 类成员和方法声明
        };
        
        • ​Derived​​ 是派生类的名称。
        • ​access-specifier​​ 可以是 public​​、protected​​ 或 private​​,用于指定基类成员的访问权限。
        • ​Base1, Base2, ..., BaseN​​ 是基类的名称,用逗号分隔。
    • 多继承初始

      • 派生类的构造函数负责调用每个基类的构造函数来初始化继承的基类部分。

      • 构造函数的调用顺序与基类在派生类中的声明顺序相同,与构造函数初始化列表中的顺序无关。

      • 析构函数的调用顺序与构造函数相反,即先调用派生类的析构函数,然后是每个基类的析构函数。

        #include <iostream>class Base1
        {
        public:Base1(){std::cout << "Base1" << std::endl;}int m_Base;
        };class Base2
        {
        public:Base2(){std::cout << "Base2" << std::endl;}int m_Base;
        };class Derived : public Base1, public Base2
        {
        public:Derived(){std::cout << "Derived" << std::endl;}int m_Base;
        };int main()
        {Derived d;d.Base1::m_Base = 1;d.Base2::m_Base = 2;d.m_Base = 3;return 0;
        }
        
    • 菱形继承

      •     +---------+|  Base   |+---------+/       \/         \
        +------+   +------+
        |  D1  |   |  D2  |
        +------+   +------+\       /\     /+-------+|Derived|+-------+
        
        • ​Base​​ 是基类。
        • ​D1​​ 和 D2​​ 是直接派生类,它们分别通过不同的路径从基类继承。
        • ​Derived​​ 是最终派生类,继承自 D1​​ 和 D2​​。
      • 菱形继承问题

        • 数据冗余:由于 D1​​ 和 D2​​ 都继承自 Base​​,当 Derived​​ 继承自 D1​​ 和 D2​​ 时,Derived​​ 中会包含两份 Base​​ 类的成员。
        • 二义性:如果 Base​​ 类有一个虚函数 foo()​​,D1​​ 和 D2​​ 都覆盖了这个虚函数,Derived​​ 如何确定调用哪个版本的 foo()​​?
      • 解决菱形问题

        • 虚继承是一种特殊的继承方式,用于解决菱形继承的问题。

        • 使用关键字 virtual​​ 声明继承关系可以确保在继承链中只有一个共享的基类实例。

        • 虚继承可以避免数据冗余和二义性的问题。

          #include <iostream>// 基类 Base
          class Base {
          public:int data;
          };// D1 通过虚继承继承 Base
          class D1 : virtual public Base {
          };// D2 通过虚继承继承 Base
          class D2 : virtual public Base {
          };// Derived 继承自 D1 和 D2
          class Derived : public D1, public D2 {
          };int main() {Derived derived;derived.data = 10;std::cout << derived.data << std::endl;  // 输出:10return 0;
          }
          

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

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

相关文章

Linux ubuntu 20.4.6安装docker

这边需要在vm中使用docker&#xff0c;记录下 1.更新系统包列表 确保您的系统包列表是最新的 sudo apt update 2.安装依赖工具 sudo apt install apt-transport-https ca-certificates curl software-properties-common 3.添加Docker GPG密钥 curl -fsSL https://downloa…

Android Room数据库异常: Room cannot verify the data integrity.

文章目录 一、前言二、错误信息如下三、参考链接 一、前言 在Room数据库结构变动的情况下&#xff0c;如果没有进行Room数据库升级迁移&#xff0c;则会报错Room cannot verify the data integrity.。在实际开发过程中&#xff0c;数据库结构会经常变化&#xff0c;直到发版。…

Centos 7.9 在线安装 VirtualBox 7.0

1 访问 Linux_Downloads – Oracle VM VirtualBox 2 点击 ​the Oracle Linux repo file 复制 内容到 /etc/yum.repos.d/. 3 在 /etc/yum.repos.d/ 目录下新建 virtualbox.repo&#xff0c;复制内容到 virtualbox.repo 并 :wq 保存。 [rootlocalhost centos]# cd /etc/yum.rep…

【数据结构 05】双链表

一、原理 双链表又称双向链表&#xff0c;通常情况下是带头循环结构&#xff0c;在CSTL标准模板库中封装的<list.h>头文件就是带头双向循环链表。 特性&#xff1a;增删灵活且高效&#xff0c;支持随机增删但不支持随机访问 设计思路&#xff1a; 链表包含一个头节点h…

一维差分(c++题解)

题目描述 输入一个长度为n的整数序列。 接下来输入 个操作&#xff0c;每个操作包含三个整数 &#xff0c;表示将序列中 之间的每个数加上 。 请你输出进行完所有操作后的序列。 输入格式 第一行包含两个整数 n 和 m。 第二行包含n个整数&#xff0c;表示整数序列。 接下来…

【Linux】模拟实现一个简单的minishell

目录 从显示屏获取输入字符流 分割字符串 取出命令名称及选项 去除输入时多按的那个换行符 创建子进程&#xff0c;实现程序替换 如果替换失败&#xff0c;进程终止exit 查看子进程情况 实现echo $?功能 实现cd 最终代码 基本思路 让父进程创建一个子进程&#xff0c…

我用Java写了一个简单的二叉树算法

二叉树是一种常见的数据结构&#xff0c;它是由节点和连接节点的边组成的。每个节点最多有两个子节点&#xff0c;分别称为左子节点和右子节点。二叉树算法包括遍历、查找、插入、删除等操作。 class Node {int data;Node left, right;public Node(int item) {data item;left…

[NCTF2019]Fake XML cookbook(特详解)

先试了一下弱口令&#xff0c;哈哈习惯了 查看页面源码发现xml function doLogin(){var username $("#username").val();var password $("#password").val();if(username "" || password ""){alert("Please enter the usern…

微信小程序如何调整checkbox和radios选择框的大小和样式

目录 修改选中框大小 修改Checkbox样式 修改Radio样式 修改选中框大小 直接使用width和height调整checkbox和radios选择框的大小是无效的,简单的调整大小可以通过修改transform值,如下所示: .wxss .fill-checkbox{transform: scale(0.5,0.5); } scale参数分别为在长…

二分查找(c++题解)

题目描述 在一个单调递增的序列里查找X。 如果找到x&#xff0c;则返回x在数组中的位置 如果没有找到&#xff0c;则返回&#xff0d;1 输入格式 第1行&#xff1a;1个整数N(1<N<2000000), 表示元素的个数 第2行开始的若干行&#xff0c;每行10个空格分开的整数&am…

SpringMVC处理ajax请求(@RequestBody注解),ajax向后端传递的数据格式详解

目录 RequestBody注解简单介绍 RequestBody获取json格式的请求参数 Servlet方式处理ajax请求 本文讲解两种方式实现SpringMVC与Ajax交互&#xff0c;1、通过SpringMVC提供的注解RequestBody处理ajax请求&#xff1b;2、通过JavaEE时期的Servlet来处理 RequestBody注解简单介…

CISAW和CISP-PTE证书选择指南

&#x1f4e3;在信息安全领域&#xff0c;选择合适的证书可以为你的职业生涯增添光彩。很多从事信息渗透行业的朋友经常讨论CISP-PTE和CISAW之间的选择问题。今天就从4个方面带你详细了解这两张证书&#xff0c;帮你做出明智的选择&#xff01; 1️⃣证书的行业前景 &#x1f4…

【数据结构1-4】图的基本应用

一、【P5318】查找文献&#xff08;邻接表DFSBFS&#xff09; 本题是图的遍历模板题&#xff0c;需要使用DFS和BFS遍历方法。 由于本题最多有1e5个顶点&#xff0c;如果采用邻接矩阵存储图的话需要4*1e10 Byte空间&#xff0c;显然会超内存&#xff0c;因此这里采用邻接表的方法…

【Nuxt3】layouts的使用

简言 Nuxt 提供了一个布局框架&#xff0c;用于将常见的 UI 模式提取为可重用的布局。 为了获得最佳性能&#xff0c;在使用时&#xff0c;放置在此目录中的组件将通过异步导入自动加载。 layouts layouts文件夹存放的是ui布局文件&#xff0c;就是实现一个页面整体架构规则的…

线程池相关的类学习

Executor public interface Executor {//执行任务void execute(Runnable command); }ExecutorService public interface ExecutorService extends Executor {//关闭线程池&#xff0c;不能再向线程池中提交任务&#xff0c;已存在与线程池中的任务会继续执行&#xff0c;直到…

超声波风速风向仪的工作原理

TH-WQX2在我们的日常生活中&#xff0c;气象条件的影响无处不在。无论是准备户外活动&#xff0c;还是安排农业生产&#xff0c;了解当天的风速和风向都是非常关键的。随着科技的发展&#xff0c;超声波风速风向仪的出现为气象监测带来了革命性的变化。 一、超声波风速风向仪的…

聚合支付,实现支付宝微信扫二维码直接跳转支付

具体要实现的功能&#xff1a;手机支付宝或微信扫描同一个二维码&#xff0c;跳转各自的支付 微信使用&#xff1a;jsapi支付 支付宝&#xff1a;wappay 上篇已写了如何实现内网穿透调试就不多叙述 1.判断客户端类型&#xff0c;从request的中将user-agent拉取下来&#xf…

【Javaweb】【C00157】基于SSM的宠物护理预定系统(论文+PPT)

基于SSM的宠物护理预定系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的宠物护理预订系统 本系统分为前台系统模块、后台管理员模块以及后台会员用户模块 其中前台系统模块&#xff1a;当游客打开系统的网址后&…

springboot(ssm同城上门喂遛宠物系统 宠物预约系统Java系统

springboot(ssm同城上门喂遛宠物系统 宠物预约系统Java系统 开发语言&#xff1a;Java 框架&#xff1a;springboot&#xff08;可改ssm&#xff09; vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&a…

报错找不到msvcp140.dll怎么办?msvcp140.dll缺失的详细修复指南

在计算机系统运行过程中&#xff0c;如果出现msvcp140.dll文件丢失的情况&#xff0c;可能会引发一系列显著的问题和故障。首先&#xff0c;我们需要理解msvcp140.dll究竟是什么以及它在操作系统中扮演何种角色。msvcp140.dll是一个动态链接库&#xff08;DLL&#xff09;文件&…