【C++ 学习 ⑯】- 继承(上)

目录

一、继承的概念和定义

1.1 - 概念

1.2 - 定义

二、继承时的对象内存模型

三、向上转型和向下转型

四、继承时的名字遮蔽问题

4.1 - 有成员变量遮蔽时的内存分布

4.2 - 重名的基类成员函数和派生类成员函数不构成重载


 


一、继承的概念和定义

1.1 - 概念

C++ 中的继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承类似,例如儿子继承父亲的财产。

继承(Inheritance)可以理解为一个类从另一个类获取成员变量和成员函数的过程,例如类 B 继承于类 A,那么 B 就拥有 A 的成员变量和成员函数。

被继承的类称为父类或基类,继承的类称为子类或派生类。"父类" 和 "子类" 通常放在一起称呼,"基类" 和 "派生类" 通常放在一起称呼。

#include <iostream>
using namespace std;
​
// 基类 Person
class Person
{
public:void SetName(const char* name = "张三") { _name = name; }
​void SetAge(int age = 18) { _age = age; }
​const char* GetName() const { return _name.c_str(); }
​int GetAge() const { return _age; }
protected:string _name;  // 姓名int _age;  // 年龄
};
​
// 派生类 Student
class Student : public Person
{
public:void SetStuId(int stu_id = 0) { _stu_id = stu_id; }
​int GetStuId() const { return _stu_id; }
protected:int _stu_id;  // 学号
};
​
// 派生类 Teacher
class Teacher : public Person
{
public:void SetJobId(int job_id) { _job_id = job_id; }
​int GetJobId() const { return _job_id; }
protected:int _job_id;  // 工号
};
​
int main()
{Student s;s.SetName("李四");s.SetAge(19);s.SetStuId(1);cout << s.GetName() << "的年龄是" << s.GetAge() << ",学号是" << s.GetStuId() << "。" << endl;// 李四的年龄是19,学号是1。
​Teacher t;t.SetName("王五");t.SetAge(30);t.SetJobId(10);cout << t.GetName() << "的年龄是" << t.GetAge() <<",工号是" << t.GetJobId() << "。" << endl;// 王五的年龄是30, 工号是10。return 0;
}

通过以上的例子我们就可以明白:继承机制是面向对象程序中使代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。继承呈现了面向对象呈现设计的层次结构,体现了由简单到复杂的认知过程,以前我们接触的复用是模板,继承则是类设计层次的复用

1.2 - 定义

继承的定义格式为

class 派生类名 : [继承方式] 基类名
{派生类新增加的成员
}

不同的继承方式会影响基类成员在派生类中的访问权限

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

总结

  1. 不管继承方式如何,基类中的 private 成员在派生类中始终不能使用(不能在派生类的成员函数中访问和调用)。注意:我们说的是基类的 private 成员不能在派生类中使用,并没有说基类的 private 成员不能被继承。实际上,基类的 private 成员是能够被继承的,并且成员变量会占用派生类对象的内存,它只是在派生类中无法使用罢了,即不可见

  2. 如果希望基类的成员既不向外暴露,即不能通过对象访问,还能在派生类中使用,那么只能声明为 protected,由此可以看出 protected 访问限定符是因为继承才出现的

  3. 通过上面的表格我们可以发现,基类的 private 成员在派生类中不可见,基类的其他成员在派生类中的访问权限 = min(成员在基类中的访问权限,继承方式),比较规则是:public > protected > private

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

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


二、继承时的对象内存模型

没有继承时,对象内存模型很简单,对象的内存中只包含成员变量,存储在栈区或堆区(使用 new 创建对象),成员函数与对象内存分离,存储在代码区。

当有继承关系时,派生类对象的内存模型可以看成是基类成员变量和新增成员变量的总和,而所有成员函数仍然存储在另外一个区域——代码区,由所有对象共享

#include <iostream>
using namespace std;
​
class A
{
public:int _i;int _j;
};
​
class B : public A
{
public:int _k;
};
​
class C : public B
{
public:int _l;
};
​
int main()
{A a;cout << &a << endl;cout << &a._i << " " << &a._j << endl;cout << sizeof(A) << endl; 
​B b;cout << &b << endl;cout << &b._i << " " << &b._j << " " << &b._k << endl;cout << sizeof(B) << endl;  
​C c;cout << &c << endl;cout << &c._i << " " << &c._j << " " << &c._k << " " << &c._l << endl;cout << sizeof(C) << endl;return 0;
}

即:

可以发现,基类的成员变量排在前面,新增的成员变量排在后面


三、向上转型和向下转型

可以用 "派生类对象/派生类指针/派生类引用" 给 "基类对象/基类指针/基类引用" 初始化或赋值,这在 C++ 中称为向上转型(Upcasting),还有一个形象的说法叫切片或切割。向上转型非常安全,可以由编译器自动完成

相应地,用基类给派生类初始化或赋值称为向下转型(Downcasting)。向下转型则有风险,需要程序员手动干预

  1. 不能用 "基类对象" 给 "派生类对象" 初始化或赋值,因为基类不包含派生类的成员变量,所以无法对派生类的成员变量赋值

  2. "基类指针或者引用" 可以通过强制类型转换的方式给 "派生类指针或引用" 初始化或赋值,但是必须是 "基类指针" 指向 "派生类对象" 时才是安全的。这里基类如果是多态类型,可以使用 RTTI(Run-Time Type Information)的 dynamic_cast 识别后进行安全转换(后面再详细讲解)。

#include <iostream>
using namespace std;
​
class Person
{
protected:string _name;int _age;
};
​
class Student : public Person
{
public:int _stu_id;
};
​
int main()
{Student s;Person p = s;Person* pp = &s;Person& rp = s;  // 由此可以发现,向上转型的过程中没有发生隐式类型转换
​// s = p;  // error
​Student* ps = (Student*)pp;  ps->_stu_id = 1; // 基类指针 pp 指向的是派生类对象 s,所以转换是安全的
​// pp = &p;// ps = (Student*)pp;// ps->_stu_id = 0;  // 越界访问// 这种情况下,强制类型转换是可行的,但不安全return 0;
}


四、继承时的名字遮蔽问题

如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,那么就会遮蔽从基类继承过来的成员。所谓遮蔽,就是在派生类中使用,以及通过派生对象访问该成员时,实际上使用的是派生类新增的成员,而不是从基类继承来的

#include <iostream>
using namespace std;
​
class A
{
public:void Print() { cout << _i << " " << _j << endl; }
public:int _i = 1;int _j = 2;
};
​
class B : public A
{
public:// 遮蔽基类的成员函数void Print() { cout << _i << " " << _j << " " << _k << endl; }
public:int _j = 3;  // 遮蔽基类的成员变量int _k = 4; 
};
​
int main()
{B b;// 使用的是派生类新增的成员,而不是从基类继承来的cout << b._j << endl;  // 3b.Print();  // 1 3 4
​// 使用的是基类继承来的成员cout << b.A::_j << endl;  // 2b.A::Print();  // 1 2return 0;
}

4.1 - 有成员变量遮蔽时的内存分布

cout << &b._i << " " << &b.A::_j << " " << &b._j << " " << &b._k << endl;
cout << sizeof(B) << endl;

即:

当基类 A 的成员变量被遮蔽时,仍然会留在派生类对象 b 的内存中,B 类新增的成员变量也始终排在基类 A 的成员变量的后面

4.2 - 重名的基类成员函数和派生类成员函数不构成重载

类其实是一种作用域,每个类都会定义它自己的作用域,在这个作用域类我们再定义类的成员。当存在继承关系时,派生类的作用域嵌套在基类的作用域之内,如果一个名字在派生类的作用域内无法找到,编译器会继续到外层的基类作用域中查找该名字的定义

在上面的例子中,B 继承自 A,它们作用域嵌套的嵌套关系如下:

函数重载指的是在同一个作用域内有多个名称相同但参数列表不同的函数,所以重名的基类成员函数和派生类成员函数不会构成重载,即便参数一样,也不会报错

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

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

相关文章

【Leetcode】移动零

移动零 题目描述算法描述编程代码 链接: 移动零 题目描述 算法描述 编程代码 class Solution { public:void moveZeroes(vector<int>& nums) {//题目要求不可以复制数组&#xff0c;开辟额外空间int dest -1,curr 0;for(;curr < nums.size();curr){if(nums[cu…

问道管理:机器人概念走势活跃,新时达涨停,拓斯达、丰立智能等大涨

机器人概念17日盘中走势活跃&#xff0c;到发稿&#xff0c;拓斯达大涨18%&#xff0c;昊志机电涨近16%&#xff0c;丰立智能涨超13%&#xff0c;步科股份、优德精细涨超10%&#xff0c;新时达涨停&#xff0c;天玑科技、兆龙互联、中大力德涨逾9%。 消息面上&#xff0c;8月16…

Eureka注册中心

全部流程 注册服务中心 添加maven依赖 <!--引用注册中心--> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> 配置Eureka 因为自…

记录:ubuntu20.04+ORB_SLAM2_with_pointcloud_map+ROS noetic

由于相机实时在线运行需要ROS&#xff0c;但Ubuntu22.04只支持ROS2&#xff0c;于是重装Ubuntu20.04。上一篇文章跑通的是官方版本的ORB_SLAM2&#xff0c;不支持点云显示。高翔修改版本支持RGB-D相机的点云显示功能。 高翔修改版本ORB_SLAM2&#xff1a;https://github.com/ga…

算法通关村第九关——中序遍历与搜索树

1 中序遍历和搜索树原理 二叉搜索树按照中序遍历正好是一个递增序列。其比较规范的定义是&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有节点的值均小于它的根节点的值&#xff1b;若它的右子树不为空&#xff0c;则右子树所有节点的值均大于它的根节点的值&…

Linux TCP编程流程

一、TCP编程流程 TCP 提供的是面向连接的、可靠的、字节流服务。TCP的服务器端和客户端编程流程如下&#xff1a; 1.socket()方法 用来创建一个套接字&#xff0c;有了套接字就可以通过网络进行数据的收发。这也是为什么进行网络通信的程序首先要创建一个套接字。创建套接字时…

GB28181视频监控国标平台EasyGBS角色绑定设备通道的功能优化

GB28181视频监控国标平台EasyGBS是基于国标GB28181协议、支持多路设备同时接入的视频监控/视频云服务平台&#xff0c;支持对多平台、多终端分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。国标GB28181平台EasyGBS可提供视频直播监控、云端录像、云存储、检索回放、智能告警…

基于CentOS搭建私有仓库harbor

环境&#xff1a; 操作系统&#xff1a;CentOS Linux 7 (Core) 内核&#xff1a; Linux 3.10.0-1160.el7.x86_64 目录 安装搭建harbor &#xff08;1&#xff09;安装docker编排工具docker compose &#xff08;2&#xff09;下载Harbor 安装包 &#xff08;3&…

OpenCV 中的色彩空间 (C++ / Python)

在本教程中,我们将了解计算机视觉中使用的流行色彩空间,并将其用于基于颜色的分割。我们还将分享 C++ 和 Python 的演示代码。

THUHCSI人机语音交互实验室9篇论文被语音旗舰国际会议INTERSPEECH录用

2023年ISCA国际语音通讯学会年会&#xff08;2023 Annual Conference of the International Speech Communication Association, INTERSPEECH 2023&#xff09;将于2023年8月20日-24日在爱尔兰都柏林召开&#xff0c;清华大学人机语音交互实验室&#xff08;THUHCSI&#xff09…

SpringCloud教程 | 第四篇:断路器(Hystrix)

在微服务架构中&#xff0c;根据业务来拆分成一个个的服务&#xff0c;服务与服务之间可以相互调用&#xff08;RPC&#xff09;&#xff0c;在Spring Cloud可以用RestTemplateRibbon和Feign来调用。为了保证其高可用&#xff0c;单个服务通常会集群部署。由于网络原因或者自身…

CentOS7安装部署Doris

文章目录 CentOS7安装部署Doris一、前言1.简介2.环境 二、正文1.Doris基础1&#xff09;架构图2&#xff09;通讯端口 2.部署服务器3.安装基础环境1&#xff09;安装JDK 112&#xff09;安装GCC3&#xff09;设置文件句柄数4&#xff09;关闭交换分区&#xff08;swap&#xff…

Ruoyi安装部署(linux环境、前后端不分离版本)

目录 简介 1 新建目录 2 安装jdk 2.1 jdk下载 2.2 解压并移动文件夹到/data/service目录 2.3 配置环境变量 3 安装maven 3.1 进入官网下载最新的maven 3.2 解压并移动文件夹到/data//service目录 3.3 配置环境变量 3.4 配置本地仓库地址与阿里云镜像 4 安装git 4.…

SQL窗口函数

1、什么是窗口函数 窗口函数可以看作是在分区对记录执行操作的函数&#xff0c;窗口函数功能与group by相似&#xff0c;但不会改变记录行数&#xff0c;因此常用于排名&#xff0c;TopN操作。 2.窗口函数语法形式 窗口函数 over ([partition by 字段名] [order by 字段名]) …

java开源 VR全景商城 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城 短视频商城 小程序商城搭建 bbc

​ 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前…

湖北黄石三维扫描文物保护修复文物建模3d打印-CASAIM中科广电

三维激光扫描技术在博物馆领域的运用&#xff0c;主要在以下3个方面&#xff1a;文物保护、文物数字化、虚拟博物馆。随着时间的流逝和人类活动的影响&#xff0c;文物不可避免地会受到来自自然或者人为的侵蚀和破坏。由于CASAIM三维激光扫描技术具有不用接触被测量目标、扫描速…

【AIGC】AI工具合集人脸动漫化,老照片修复和视频补帧工具

Paper2GUI: 一款面向普通人的 AI 桌面 APP 工具箱&#xff0c;免安装即开即用&#xff0c;已支持 40AI 模型&#xff0c;内容涵盖 AI 绘画、语音合成、视频补帧、视频超分、目标检测、图片风格化、OCR 识别等领域。支持 Windows、Mac、Linux 系统。 小白兔AI 3.0版起永久免费A…

如何使用NLP库解析Python中的文本

Python是一种强大的面向对象的编程&#xff08;object-oriented programming&#xff0c;OOP&#xff09;语言&#xff0c;在人工智能领域有着广泛的用途。正是鉴于其实用性&#xff0c;以Google为首的大型科技公司&#xff0c;已经对其开发了Tensorflow等代码库&#xff0c;帮…

机器人制作开源方案 | 滑板助力器

我们可以用一块废滑板做些什么呢&#xff1f; 如今&#xff0c;越来越多的人选择电动滑板作为代步工具或娱乐方式&#xff0c;市场上也涌现出越来越多的电动滑板产品。 &#xff08;图片来源&#xff1a;Backfire Zealot X Belt Drive Electric Skateboard– Backfire Board…

开源TTS+gtx1080+cuda11.7+conda+python3.9吊打百度TTS

一、简介 开源项目&#xff0c;文本提示的生成音频模型 https://github.com/suno-ai/bark Bark是由Suno创建的基于变换器的文本到音频模型。Bark可以生成极为逼真的多语种演讲以及其他音频 - 包括音乐、背景噪音和简单的声音效果。该模型还可以产生非言语沟通&#xff0c;如…