【C++】继承的理解

1.继承的概念和定义

1.1继承的概念

继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段,它允许程序员在 持原有类特性的基础上进行扩展 ,增加功能,这样产生新的类,称派生类。继承 呈现了面向对象 程序设计的层次结构 ,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用, 承是类设计层次的复用。
#include <iostream>
class A
{
public:int ma;
private:int mb;
protected:int mc;
};class B : public A	// 继承:A叫做基类/父类	B叫派生类/子类
{
public:void func(){std::cout << ma << std::endl;}int md;
private:int me;
protected:int mf;
};int main()
{A a;B b;std::cout << sizeof(a) << std::endl;	// 12std::cout << sizeof(b) << std::endl;	// 24
}

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

 

总结:

 1.基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私 有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它

2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在 派生类中能访问,就定义为protected可以看出保护成员限定符是因继承才出现的

3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式)public > protected > private。

4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public不过 最好显示的写出继承方式

5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡 使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里 面使用,实际中扩展维护性不强

1.3默认的继承方式? 

class定义的默认private,struct定义的默认public的.

1.4派生类的构造函数

派生类可以继承基类的构造函数和析构函数,用来初始化和释放从基类继承来的成员变量
派生类的构造函数和析构函数,负责初始化和清理派生类
派生来从基类继承来的成员的初始化和清理由基类的构造函数和析构函数来负责。

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

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
  • 或者切割。寓意把派生类中父类那部分切来赋值过去。
  • 基类对象不能赋值给派生类对象。
  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
  • 的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run
  • Time Type Information)dynamic_cast 来进行识别后进行安全转换。
#include <iostream>
/*
1.把继承结构,也说成从上(基类)到下(派生类)的结构
2.基类对象 -> 派生类对象派生类对象 -> 基类对象基类指针(引用) -> 派生类对象派生类指针(引用) -> 基类对象总结:在继承结构中进行上下的类型转换,默认支持从下到上的类型的转换
*/class Base
{
public:Base(int data = 10) :ma(data){}void show(){std::cout << "Base:show()" << std::endl;}void show(int){std::cout << "Base:show(int)" << std::endl;}
private:int ma;
};class Derive : public Base
{
public:Derive(int data = 20):Base(data), mb(data){}void show(){std::cout << "Derivr::show()" << std::endl;}
private:int mb;
};int main()
{Base b(10);Derive d(20);// 基类对象 <- 派生类对象		类型从下到上的转换	Yb = d;// 派生类对象 <- 基类对象		类型从上到下的转换	N// d = b;// 基类指针(引用)<- 派生类对象	类型从下到上的转换	Y// 只能访问派生类继承的基类的那部分内容Base* pb = &d;pb->show();pb->show(10);((Derive*)pb)->show();	// 类型强转为派生类的指针就可以访问派生类的内容// 派生类指针(引用)<- 基类对象	类型从上到下的转换	N/*Derive* pd = (Derive*) & b;	不安全,涉及了内存的非法访问pd->show();*/d.show();d.Base::show(10);// d.show(20);	// 优先找的是派生类自己作用域的show名字成员;没有的话才去基类里面找return 0;
}

3.重载、隐藏、覆盖


    1.重载关系    
    一组函数要重载,必须处在同一个作用域中;而且函数名字相同,参数列表不同

    2.隐藏关系
    在继承结构当中,派生类的同名成员把基类的同名成员给隐藏调用了

    3.覆盖关系/相当于重写方法
    虚函数表中虚函数地址的覆盖

4.虚函数、静态绑定、动态绑定

#include <iostream>
#include <typeinfo>class Base
{
public:Base(int data = 10) :ma(data){}// 虚函数virtual void show(){std::cout << "Base:show()" << std::endl;}// 虚函数virtual void show(int){std::cout << "Base:show(int)" << std::endl;}
private:int ma;
};class Derive : public Base
{
public:Derive(int data = 20) :Base(data), mb(data){}void show(){std::cout << "Derivr::show()" << std::endl;}
private:int mb;
};int main()
{Derive d(50);Base* pb = &d;/*pb是基类类型	Base::show如果发现show是普通函数,就进行静态绑定call Base::show()如果发现pb是基类类型,编译阶段在Base类中去看show函数,发现是虚函数,就进行动态绑定了*/pb->show();		// 静态(编译时期)的绑定(函数的调用)pb->show(10);std::cout << sizeof(Base) << std::endl;std::cout << sizeof(Derive) << std::endl;/*pb的类型:Base -> 有没有虚函数如果Base没有虚函数,*pb识别的就是编译时期的类型, *pb -> Base类型如果Base有虚函数,*pb识别的就是运行时期的类型 RTTI 类型 "calss Derive"*/std::cout << typeid(pb).name() << std::endl;std::cout << typeid(*pb).name() << std::endl;return 0;
}

总结一:
如果一个类里面定义了虚函数,那么编译阶段,编译器给这个类类型产生了一个唯一的vftable虚函数表,
虚函数表中主要存储的内容就是RTTI指针和虚函数的地址。
RTTI:run-time type information 运行时的类型信息    指向类型字符串,例如我的类是Base那么&RTTI指向"Base"
当程序运行时,每一张虚函数表都会加载到内存的.rodata区,是个只读数据区,也叫常量区。

总结二:
一个类里面定义了虚函数,那么这个类定义的对象。其运行时,内存中开始部分,多存储一个vfptr虚函数指针,指向
类型的虚函数表vftable。一个类型定义的n个对象,它们的vfptr指向的都是同一张虚函数表。

总结三:
一个类里面虚函数的个数,不影响对象内存大小,都是多一个虚函数指针(vfptr    4个字节),影响的是虚函数表的大小

总结四:
如果中的方法和基类继承来的某个方法,返回值,函数名,参数列表都相同,而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数。这两个函数的关系是覆盖的关系,相当于重写了这个函数

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

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

 6.继承和友元

友元关系不能继承 ,也就是说基类友元不能访问子类私有和保护成员
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);
}

7.继承与静态成员

基类定义了 static 静态成员,则整个继承体系里面只有一个这样的成员 。无论派生出多少个子
类,都只有一个 static 成员实例
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;Student ::_count = 0;cout <<" 人数 :"<< Person ::_count << endl;
}

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

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

相关文章

C++ 详细讲解 洛谷P1428 小鱼比可爱

&#xff08;其实这道题难度不高&#xff0c;但是博主正在适应c语言加上这道题目太可爱了所以忍不住发发~&#xff09; 目录 1.题目要求 2.题目解读 3.代码实现 1.题目要求 2.题目解读 这道题需要使用c中的容器储存小鱼的可爱程度和不如自己可爱的小鱼的数目&#xff0c;…

Android亮屏Job的功耗优化方案

摘要: Job运行时会带来持锁的现象,目前灭屏放电Job的锁托管已经有doze和绿盟标准监管,但是亮屏时仍旧存在过长的持锁现象,故为了优化功耗和不影响用户体验下,新增亮屏放电下如果满足冻结和已运行过一次Job,则进行job限制,当非冻结时恢复的策略 1.现象: (gms_schedu…

Spring1(初始Spring 解耦实现 SpringIOC SpringDI Spring常见面试题)

Spring1 创建项目集成maven创建一个Maven项目实现&#xff1a; 初识SpringSpring简介Spring的发展历史Spring之父体系结构生态系统官方文档解耦实现JDBCSpringBoot整合MyBatis和lombok&#xff0c;开启驼峰映射三层思想 SpringIOC实现 SpringDIset注入全部代码&#xff1a;实现…

服务器新建用户

文章目录 前言一、步骤二、问题三、赋予管理员权限总结 前言 环境&#xff1a; 一、步骤 创建用户需要管理员权限sudo sudo useradd tang为用户设置密码 sudo passwd tang设置密码后&#xff0c;可以尝试使用 su 切换到 tang 用户&#xff0c;确保该用户可以正常使用&#…

leetcode-88-合并两个有序数组

题解&#xff1a; 解法一&#xff1a;从后向前同时遍历两个数组&#xff0c;因为nums1后面是0&#xff0c;从后遍历节省空间。 1、定义三个指针&#xff0c;分别为&#xff1a;len1m-1指向nums1的最后一个非0数字&#xff1b;len2n-1指向nums2的最后一个数字&#xff1b;len3…

操作系统(10) (并发(2)------基于软件/硬件/操作系统层面解决两个进程之间的临界区问题/抢占式/非抢占式内核)

目录 1. 基于软件层面(Petersons Solution) Petersons Solution 满足三个要求: 好处: 缺点 2. 基于硬件层面 1. Disabling Interrupts (禁用中断) 概念解释&#xff1a; 代码框架&#xff1a; 要求&#xff1a; 禁用中断的好处与问题&#xff1a; 2. Test and Set Lock (…

Java | Leetcode Java题解之第526题优美的排列

题目&#xff1a; 题解&#xff1a; class Solution {public int countArrangement(int n) {int[] f new int[1 << n];f[0] 1;for (int mask 1; mask < (1 << n); mask) {int num Integer.bitCount(mask);for (int i 0; i < n; i) {if ((mask & (1…

2024年大厂AI大模型面试题精选与答案解析

前言 随着AI市场&#xff0c;人工智能的爆火&#xff0c;在接下来的金九银十招聘高峰期&#xff0c;各大科技巨头和国有企业将会对AGI人才的争夺展开一场大战&#xff0c;为求职市场注入了新的活力。 为了助力求职者在面试中展现最佳状态&#xff0c;深入理解行业巨头的选拔标…

智能网联汽车:人工智能与汽车行业的深度融合

内容概要 在这个快速发展的时代&#xff0c;智能网联汽车已经不再是科幻电影的专利&#xff0c;它正在悄然走进我们的日常生活。如今&#xff0c;人工智能&#xff08;AI&#xff09;技术与汽车行业的结合犹如一场科技盛宴&#xff0c;让我们看到了未来出行的新方向。通过自动…

AI大模型重塑软件开发:从代码自动生成到智能测试

随着AI技术的不断发展&#xff0c;AI大模型在软件开发领域的应用日益广泛。从代码自动生成到智能测试&#xff0c;AI大模型正在深刻改变着软件开发的各个环节&#xff0c;重塑着整个开发流程。本文将探讨AI大模型的定义、应用场景、优势以及挑战&#xff0c;并展望未来的发展趋…

【基础】os模块

前言 1、os是operation system&#xff08;操作系统&#xff09;的缩写&#xff1b;os模块就是python对操作系统操作接口的封装。os模块提供了多数操作系统的功能接口函数。&#xff08;OS模块提供了与操作系统进行交互的函数&#xff09; 2、操作系统属于Python的标准实用程…

算法学习027 c++蛇形三角形填充 二维数组常规应用 中小学算法思维学习 比赛算法题解 信奥算法解析

目录 C蛇形三角形填充 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、运行结果 五、考点分析 六、推荐资料 C蛇形三角形填充 一、题目要求 1、编程实现 输入一个正整数N&#xff0c;输出N行的蛇形数字三角形&#xff08;见输出样例&#xf…

[vulnhub]DC: 1

https://www.vulnhub.com/entry/dc-1,292/ 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的&#xff0c;所以靶机IP是156 nmap -sP 192.168.75.0/24 // Starting Nmap 7.93 ( https://nmap.org ) at 2024-09-28 12:48 CST Nmap scan rep…

PyQt5的安装与简介

目录 一、介绍 二、PyQt5的安装 1、安装PyQt5 2、安装Qt的工具包 三、配置Qt工具 1、配置Designer &#xff08;1)、打开pycharm&#xff0c;找到设置选项 &#xff08;2&#xff09;、找到工具-->外部工具 &#xff08;3&#xff09;、点击号&#xff0c;创建外部工…

「Qt Widget中文示例指南」如何实现窗口嵌入?

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文中的示例主要演…

clickhouse运维篇(三):生产环境一键生成配置并快速部署ck集群

前提条件&#xff1a;先了解集群搭建流程是什么样&#xff0c;需要改哪些配置&#xff0c;有哪些环境&#xff0c;这个文章目的是简化部署。 clickhouse运维篇&#xff08;一&#xff09;&#xff1a;docker-compose 快速部署clickhouse集群 clickhouse运维篇&#xff08;二&am…

【OpenGL】vs中glsl高亮显示插件

vs中glsl高亮显示插件 扩展搜索glsl安装

<项目代码>YOLOv8 猫狗识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

jmeter脚本-请求体设置变量and请求体太长的处理

目录 1、查询接口 1.1 准备组织列表的TXT文件&#xff0c;如下&#xff1a; 1.2 添加 CSV数据文件设置 &#xff0c;如下&#xff1a; 1.3 接口请求体设置变量&#xff0c;如下&#xff1a; 2、创建接口 2.1 见1.1 2.2 见1.2 2.3 准备创建接口的请求体TXT文件&#xff…

哔哩哔哩车机版2.7.0|专为司机打造的车机版B站,内容丰富,功能齐全

哔哩哔哩车机版是一款专为司机朋友们打造的车机版应用&#xff0c;扫码登录即可使用。该软件让你通过耳朵了解最新的游戏、动画动漫信息&#xff0c;感受其独特的趣味性内容。车机版亮点包括二次元和三次元的鬼畜视频、原创和翻唱音乐、前沿科技科普、国内外优秀舞蹈作品等。软…