继承(上):基类和派生类对象赋值转换,继承中的作用域,派生类的默认成员函数

1.继承的概念及定义

1.1继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在
持原有类特性的基础上
进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象
程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继
承是类设计层次的复用。

1.2 继承定义

1.2.1定义格式

下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类

可以称派生类为子类,基类为父类。 

class Person
{
public:
    void Print()
    {
        cout << "name:" << _name << endl;
        cout << "age:" << _age << endl;
    }
protected:
    string _name = "peter"; // 姓名
    int _age = 18; // 年龄
};
// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。
//这里体现出了Student和Teacher复用了Person的成员。
class Student : public Person
{
protected:
    int _stuid; // 学号
};

class Teacher : public Person
{
protected:
    int _jobid; // 工号
};

int main()
{
    Student s;
    Teacher t;
    s.Print();
    t.Print();
    return 0;
}

 既然 student 是person 的派生类那它们调用 print()函数的时候是同一个函数吗?

从反汇编代码可以看到它们调用的是同一个函数。

 

1.2.2继承关系和访问限定符

1.2.3继承基类成员访问方式的变化

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected
成员
派生类的private
成员
基类的protected
成员
派生类的protected
成员
派生类的protected
成员
派生类的private
成员
基类的private成
在派生类中不可见在派生类中不可见在派生类中不可 见

什么是不可见呢?派生类用不了就是不可见,如下:

基类的私有对派生类不可见,但是可以通过其他的方式来间接调用基类的私有成员。

  •  基类的protected成员public继承:

但是基类的保护成员公有继承之后,在类外面是不能直接访问的,有点类似私有。如下: 

总结:
1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私

有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都

不能去访问它。
2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在

派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他

成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected

> private。
4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过

‘最好显示的写出继承方式。(一般情况继承方式还是要显示写出来的)
5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡

使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里

面使用,实际中扩展维护性不强

2.基类和派生类对象赋值转换

在把 s  赋值给 ps 过程中有没有发生类型转换呢。

 

  •  变量i 发生截断时,是会产生临时变量的如下。(因为临时变量具有常性)

加 const 才能编译通过。

 而在把 s  赋值给 ps 过程中,它与下面的机制不一样的,他并不是类型转换,它是能编译通过的。

它的特殊语法规则: 不是类型转换,中间没有产生临时变量。

 

 

  • 结论:派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

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

如下:

 

 3.继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域。
2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
4. 注意在实际中在继承体系里面最好不要定义同名的成员。

  • 如下调用print() 函数他会先到子类中找,子类中找不到再到父类中寻找。


我们知道子类和父类处于两个不同的域,所以它们就可以有同名变量,

那么如果子类和父类中都有一个同名成员会怎么办?

所以它默认访问的是自己的成员(就近原则)

如果实在想访问父类的,我们可以指定作用域。如下:  

语法上叫做重定义或隐藏。

  下面这个fun 的关系是什么呢?A.重载  B.隐藏  C.编译报错  D.运行报错 

答案:B    .重载(同一个定义域才叫重载)

4.派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类
中,这几个成员函数是如何生成的呢?

class Person
{
public:
    Person(const char* name = "peter")
        : _name(name)
    {
        cout << "Person()" << endl;
    }
    Person(const Person& p)
        : _name(p._name)
    {
        cout << "Person(const Person& p)" << endl;
    }
    Person& operator=(const Person& p)
    {
        cout << "Person operator=(const Person& p)" << endl;
        if (this != &p)
            _name = p._name;
        return *this;
    }
    ~Person()
    {
        cout << "~Person()" << endl;
    }
protected:
    string _name; // 姓名
};

class Student : public Person
{
public:
protected:
    int _num; //学号
    string _address = "china"; 
};

那父类和子类的构造函数是同一个吗?不是,派生类会调用自己的构造函数,只不过它可以调用基类的构造函数。

我们不写,编译器自动生成的构造(内置类型不处理,自定义类型调用它自己的默认构造,父类会当成一个整体调用父类的默认构造 )

我们要怎样写子类的构造呢?

如下时会报错的:

怎样调用父类的构造呢?如下:

  • 子类默认生成的构造是怎样调用呢:
  • 父类构造(整体)-- 调用父类的默认构造
  • 子类的内置成员 -- 一般不处理
  • 子类的自定义成员 -- 默认构造

  • 子类默认生成的拷贝构造是怎样调用呢:
  • 父类构造(整体)-- 调用父类的拷贝构造
  • 子类的内置成员 -- 值拷贝
  • 子类的自定义成员 -- 拷贝构造
  • 一般情况不需要自己写(涉及深拷贝就需要自己写)

自己写如下:

这样写的原因是因为(派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

  • 子类默认生成的赋值重载调用与拷贝构造类似。


    这里需要这么写(Person::operator=(st))的原因是因为子类的赋值与父类的赋值构成隐藏关系。所以这里要指定作用域。

    Student& operator=(const Student& st)
    {
        if (this != &st)
        {
            Person::operator=(st);
            _num = st._num;
            _address = st._address;
        }
        return *this;
    }
  • 子类默认生成的析构函数是怎样调用呢:
  • 父类成员--调用父类的析构
  • 子类自己的内置成员-- 不处理
  • 子类自己的自定义成员-- 调用析构

我们要怎样写呢?如下:

编译不过为什么呢?

由于多态, 析构函数名字会被统一处理成destrutor();由于子类和父类都叫destrutor(),所以他们构成隐藏。这里也需要指定作用域。

可是这里好像父类的析构居然调用了四次?

为什么这里不能显示调用父类的构造呢?

不能先父后子

因为子类析构的时候可能会用到父类的成员,先父后子可能会出问题。所以父类的析构不是自己显式调用的他会在子类析构之后自动调用。

 总结:

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
  5. 派生类对象初始化先调用基类构造再调派生类构造。
  6. 派生类对象析构清理先调用派生类析构再调基类的析构。
  7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以子类析构函数和父类析构函数构成隐藏关系。

感谢大家的观看!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PostgreSQL 如何解决数据迁移过程中的数据类型不匹配问题?

文章目录 一、了解常见的数据类型不匹配情况1. 整数类型差异2. 浮点数类型差异3. 字符类型差异4. 日期和时间类型差异 二、解决数据类型不匹配的一般策略1. 数据转换2. 调整数据库表结构3. 数据清洗和预处理 三、PostgreSQL 中的数据类型转换函数1. 数值类型转换2. 字符类型转换…

数据结构(一)C语言补

数据结构 内存空间划分 一个进程启动后&#xff0c;会生成4G的内存空间 0~3G是用户空间(应用层) 3~4G是内核空间(底层) 0~3G 3~4G 所有的进程都会共享3G~4G的内核空间&#xff0c; 但是每个进程会独立拥有0~3G的用户空间。 栈区 存放数据特点 栈区存放数据的申请空间的先后…

算法:[动态规划] 斐波那契数列模型

目录 题目一&#xff1a;第 N 个泰波那契数 题目二&#xff1a;三步问题 题目三&#xff1a;最小花费爬楼梯 题目四&#xff1a;解码方法 题目一&#xff1a;第 N 个泰波那契数 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 …

水冷液冷负载系统的六种基本类型

您可以选择六种基本类型的冷却系统&#xff0c;以满足负载的冷却需求。每个人都有其优点和缺点。本文旨在识别不同类型的冷却系统并确定它们的优缺点&#xff0c;以便您可以根据自己的需求做出明智的选择。 液体冷却系统有六种基本类型&#xff1a; 1.液对液 2.闭环干燥系统…

HackTheBox--Headless

Headless测试过程 1 信息收集 NMAP端口扫描 nmap -sSCV 10.10.11.85000端口测试 检查页面功能&#xff0c;请求 For questions 功能&#xff0c;跳转到 /support 目录 目录扫描 发现 /dashboard 目录 访问 /dashboard 目录&#xff0c;显示未认证&#xff0c;如果通过认证…

git杂记

git 安装&#xff1a; 在 Windows 上安装 Git 也有几种安装方法。 官方版本可以在 Git 官方网站下载。 打开 https://git-scm.com/download/win&#xff0c;下载会自动开始。 要注意这是一个名为 Git for Windows 的项目&#xff08;也叫做 msysGit&#xff09;&#xff0c;和…

高薪程序员必修课-JVM创建对象时如何解决多线程内存抢占问题

前言 在JVM中&#xff0c;堆的内存分配过程涉及到线程安全性的保障&#xff0c;具体来说涉及到对象的内存分配时&#xff0c;并不是简单的抢占式分配&#xff0c;而是通过一些机制来保证线程安全和高效的内存管理。下面解释一下JVM是如何设计来保证线程安全的&#xff1a; 内存…

Go语言---接口interface、接口转换、继承、类型查询

接口(interface)概念 在 Go 语言中&#xff0c;接口(interface)是一个自定义类型&#xff0c;接口类型具体描述了一系列方法的集合。 接口又称为动态数据类型&#xff0c;在进行接口使用的的时候,会将接口对位置的动态类型改为所指向的类型&#xff0c;会将动态值改成所指向类…

Kafka抛弃Zookeeper后如何启动?

Kafaka如何下载 官网地址 目前Kafka最新的版本就是3.7.1 我们可以看到下面这两个版本信息&#xff1f;什么意思呢&#xff1f; Scala 2.12 - kafka_2.12-3.7.1.tgz (asc, sha512)Scala 2.13 - kafka_2.13-3.7.1.tgz (asc, sha512) 我们应该知道&#xff0c;一个完整的Kafka实…

塑料法兰的标准

塑料法兰的标准包括国标GB/T9112-2010、化工部标准HG5010-52&#xff5e;HG5028-58、机械部标准JB81-59&#xff5e;JB86-59、以及船用生活给排水塑料管法兰的标准CB/T 4138-2011和CB/T 4454-2017。这些标准涵盖了从国家标准到特定用途&#xff08;如船用&#xff09;的详细规范…

KVM把新添加的磁盘扩容到根目录

1、对新增的磁盘进行分区&#xff08;注&#xff1a;可省略&#xff09; PS&#xff1a;使用fdisk或gdisk&#xff08;大于2T时使用&#xff09;对新增磁盘进行分区。 [rootkvm-clinet ~]# fdisk/dev/sdb Welcome to fdisk (util‐linux 2.23.2).4 Changes will remain in …

Python28-8 GBM梯度提升算法

梯度提升算法&#xff08;Gradient Boosting Machine&#xff0c;GBM&#xff09;是一种集成学习方法&#xff0c;通过逐步构建一系列简单模型&#xff08;通常是决策树&#xff09;&#xff0c;并结合这些模型来提高整体预测性能。GBM广泛用于回归和分类任务&#xff0c;因为它…

【计算机毕业设计】017基于微信小程序的学生公寓电费信息管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

51单片机嵌入式开发:1、STC89C52环境配置到点亮LED

STC89C52环境配置到点亮LED 1 环境配置1.1 硬件环境1.2 编译环境1.3 烧录环境 2 工程配置2.1 工程框架2.2 工程创建2.3 参数配置 3 点亮一个LED3.1 原理图解读3.2 代码配置3.3 演示 4 总结 1 环境配置 1.1 硬件环境 硬件环境采用“华晴电子”的MINIEL-89C开发板&#xff0c;这…

在iPhone / iPad上轻松模拟GPS位置 AnyGo for Mac

在iPhone / iPad上轻松模拟GPS位置 AnyGo for Mac AnyGo for Mac是一款专为Mac电脑用户设计的虚拟定位工具。它可以模拟你的GPS位置&#xff0c;让你的设备显示你在任何世界上的任何地方。无论你是想在游戏中虚拟移动&#xff0c;还是在社交媒体上分享虚拟的旅行照片&#xff0…

基础权限存储

一丶要求 建立用户组shengcan&#xff0c;其id为 2000建立用户组 caiwu&#xff0c;其id 为2001建立用户组 jishu&#xff0c;其id 为 2002建立目录/sc,此目录是 shengchan 部门的存储目录&#xff0c;只能被 shengchan 组的成员操作4.其他用户没有任何权限建立目录/cw,此目录…

第二周:李宏毅机器学习笔记

第二周学习周报 摘要Abstract一、深度学习1.Backpropagation&#xff08;反向传播&#xff09;1.1 链式法则1.2 Forward pass&#xff08;前向传播&#xff09;1.3 Backward pass&#xff08;向后传播&#xff09;1.4 总结 2. Regression&#xff08;神奇宝贝案例&#xff09;2…

Bug记录:【com.fasterxml.jackson.databind.exc.InvalidDefinitionException】

bug记录 序列化错误 异常com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 完整错误(主要是FAIL_ON_EMPTY_BEANS) 00:15:20.250 [http-nio-3000-exec-1] ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - S…

【漏洞复现】TerraMaster TOS exportUser.php 远程命令执行

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

Windows编程[下]

Windows编程[下] 一、线程1. 内核对象2.多线程群聊服务器3.多线程群聊客户端4.线程同步之事件对象常用函数和参数解释 二、进程三、Qt1.第一个Qt项目2.Qt助手的使用3.QPushButton简介4.Qt对象树对象树的基本概念使用对象树模式的好处对象树的问题 5.信号与槽5.1 自定义信号和槽…