当前位置: 首页 > news >正文

C++ 访问控制——公有继承、私有继承、保护继承

派生类继承了基类的全部数据成员和除了构造函数和析构函数之外的全部函数成员,但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员,其访问属性由继承方式控制。

基类的成员有public(公有)、protected(保护)和private(私有)三种访问属性。基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的对象,只能访问该类的公有成员。

类的继承方式有public(公有继承)、protected(保护继承)和private(私有继承)三种。不同的继承方式,导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。这里说的访问来自两个方面:一是派生类中新增成员访问从基类继承的成员;二是在派生类外部,通过派生类对象访问从基类继承的成员。

1.公有继承

当类的继承方式为公有继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可以直接访问。 也就是说基类的公有成员和保护成员被继承到派生类中访问属性不变,仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问它们。在类族之外只能通过派生类的对象访问从基类继承的公有成员,而无论是派生类的成员还是派生类的对象都无法直接访问基类的私有成员。

【例1】Point类公有继承
将从Point类派生出新的Rectangle(矩形)类。矩形是由一个点加上长、宽构成。矩形的点具备了Point类的全部特征,同时,矩形自身也有一些特点,就需要在继承Point类的同时添加新的成员。

程序的头文件部分如下:
Point.h文件:

#pragma once
#ifndef _POINT_H
#define _POINT_H
class Point//基类Point的定义
{
public://公有函数成员void initpoint(float x = 0, float y = 0){this->x = x;this->y = y;}void move(float ox, float oy){x += ox;y += oy;}float getX()const{return x;}float getY()const{return y;}
private://私有数据成员float x, y;
};#endif

Rectangle.h文件:

#pragma once
#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include"Point.h"
class Rectangle :public Point//派生类的定义部分
{
public:void initRectangle(float x, float y, float w, float h)//新增公有函数成员{initpoint(x, y);//调用基类公有成员函数this->w = w;this->h = h;}float getH()const{return h;}float getW()const{return w;}
private://新增私有数据成员float w, h;
};#endif

这里首先定义了基类Point。派生类Rectangle继承了Point类的全部成员(隐含的默认构造函数和析构函数除外),因此在派生类中,实际拥有的成员就是从基类继承过来的成员和派生类新定义成员的总和。继承方式为公有继承,这时,基类中的公有成员在派生类中的访问属性保持原样,派生类的成员函数及对象可以访问到基类的公有成员(例如在派生类Rectangle的函数成员initRectangle中直接调用基类的函数initpoint),但是无法访问基类的私有成员(例如x,y)。基类原有的外部接口(例如getX()和getY()函数)变成了派生类外部接口的一部分。当然,派生类自己新增的成员之间都是可以互相访问的。

Rectangle类继承了Point类的成员,也就实现了代重用,同时通过新增成员,加入了自身的独有特征,达到了程序的扩充。

程序的主函数部分如下:

#include<iostream>
using namespace std;
#include"Rectangle.h"
int main()
{Rectangle r;//定义Rectangle类的对象r.initRectangle(2, 3, 20, 10);//设置矩形的数据r.move(3, 2);//移动矩形的位置cout << "这个矩形的数据(x,y,w,h):" << endl;cout << r.getX() << "," << r.getY() << "," << r.getW() << "," << r.getH() << endl;//输出矩形的特征参数return 0;
}

运行结果:
在这里插入图片描述
分析:
主函数中首先声明了一个派生类对象r,对象生成时调用了系统所产生的默认构造函数,这个函数的功能是什么都不做。任何通过派生类的对象r访问了派生类的公有函数initRectangle,也访问了派生类从基类继承来的公有函数和move()、getX()和getY()。这样我们看到,从一个基类以公有继承的方式产生了派生类之后,在派生类的成员函数中,以及通过派生类的对象如何访问从基类继承的公有成员。

2.私有继承

当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可以直接访问。 也就是说,基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类族外部通过派生类的对象无法直接访问它们。无论是派生类成员还是通过派生类的对象,都无法直接访问从基类继承的私有成员。

经过私有继承后,所有基类的成员都成为了派生类的私有成员或不可直接访问的成员,如果进一步派生的话,基类的全部成员就无法在新的派生类中被直接访问。因此,私有继承之后,基类的成员再也无法在以后的派生类中直接发挥作用,实际上相当于中止了基类功能的继续派生,出于这种原因,一般情况私有继承使用较少。

【例2】Point类私有继承

程序类的定义部分如下:

Point.h文件:

#pragma once
#ifndef _POINT_H
#define _POINT_H
class Point//基类Point的定义
{
public://公有函数成员void initpoint(float x = 0, float y = 0){this->x = x;this->y = y;}void move(float ox, float oy){x += ox;y += oy;}float getX()const{return x;}float getY()const{return y;}
private://私有数据成员float x, y;
};#endif

Rectangle.h文件:

#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include"Point.h"
class Rectangle :private Point
{
public:void initRectangle(float x, float y, float w, float h)//新增公有函数成员{initpoint(x, y);//调用基类公有成员函数this->w = w;this->h = h;}void move(float ox, float oy){Point::move(ox, oy);}float getX()const{return Point::getX();}float getY()const{return Point::getY();}float getH()const{return h;}float getW()const{return w;}
private://新增私有数据成员float w, h;
};
#endif

派生类Rectangle继承了Point类的成员,因此在派生类中,实际拥有的成员就是从基类继承的成员与派生类新成员的总和。继承方式是私有继承,这时,基类中的公有和保护成员在派生类中都以私有成员的身份出现。派生类成员的成员函数及对象无法访问基类的私有成员(例如基类的x,y)。派生类的成员仍然可以访问到从基类继承过来的公有和保护成员(例如在派生类函数成员initRectangle中直接调用基类的函数initpoint),但是在类的外部通过派生类的对象无法访问到基类的任何成员,基类原有的外部接口(例如getX()和getY()函数)被派生类封装和隐藏起来。当然,派生类新增的成员之间仍然可以自由地相互访问。

在私有继承的情况下,为了保证基类的一部分外部接口能够在派生类中也存在,就必须在派生类中重新声明同名的成员。这里在派生类Rectangle中,重新声明了move,getX,getY等函数,利用派生类对基类成员的访问能力,把基类的原有成员函数的功能照搬过来。这种在派生类中重新声明的成员函数具有比基类同名成员函数更小的作用域,因此在调用时,根据同名隐藏的原则,自然会使用派生类的函数。

程序的主函数部分:

#include"Rectangle.h"
int main()
{Rectangle r;//定义Rectangle类的对象r.initRectangle(2, 3, 20, 10);//设置矩形的数据r.move(3, 2);//移动矩形的位置cout << "这个矩形的数据(x,y,w,h):" << endl;cout << r.getX() << "," << r.getY() << "," << r.getW() << "," << r.getH() << endl;//输出矩形的特征参数return 0;
}

运行结果:

在这里插入图片描述
分析:

主函数部分和公有继承的主函数部分的代码完全相同,但是执行过程不同。本例中的Rectangle类对象r调用的函数都是派生类自身的公有成员,因为私有继承,它不可能访问到任何一个基类的成员。和例1的Point的公有继承相比较,本例对程序修改的只是派生类的内容,基类和主函数部分没有改变。Rectangle类的外部接口不变,内部成员的实现做了改造,没有影响到程序的其他部分,这正是面向对象程序设计重用和扩充的一个实际体现。

3.保护继承

保护继承中,基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可以直接访问。 这样,派生类的其他成员就可以直接访问从基类继承来的公有和保护成员,但在类的外部通过派生类的对象无法直接访问它们。无论时派生类成员还是派生类对象都无法直接访问基类的私有成员。

比较私有继承和保护继承可以看出,实际上在直接派生类中,所有成员的访问属性都是完全相同的。但是如果派生类作为新的基类继续派生时,二者就有区别了。假设Rectangle类以私有继承的方式继承了Point类之后,Rectangle类又派生出Square类,那么Square类的成员和对象都不能访问间接从Point来中继承来的成员。如果Rectangle类是以保护继承的方式继承了Point类,那么Point类中的公有和保护成员在Retangle类中都是保护成员,Retangle类再派生出Square类之后,Point类中的公有和保护成员被Square类间接继承后,有可能是保护或者私有(根据从Rectangle到Square的派生方式不同而不同)。因而,Square类的成员有可能可以访问间接从Point类中继承来的成员。

从继承的访问规则,可以看到类中保护成员的特征。如果Point类中含有保护成员,对于建立Point类对象的模块来讲,保护成员和该类的私有成员一样是不可以访问的。如果Point类派生除了Rectangle类,则对于Rectangle类来讲,保护成员和私有成员具有相同的访问特性。换句话来说,就是Point类中的保护成员有可能被它的派生类访问,但是决不可能被其他外部使用者(程序中的普通函数、与Point类平行的其他类)访问。

这样,如果合理地利用保护成员,就可以在类的复杂层关系中在共享与成员隐藏之间找到一个平衡点,既能实现成员隐蔽,又能方便继承,实现代码的高效重用和扩充。

假定某一个类A有保护数据成员x,我们来讨论x的访问特征。

类A定义为:

class A
{
protected://保护数据成员int x;	
};

如果主函数为:

int main()
{A a;a.x=5;//错误
}

程序在编译阶段就会出错,错误原因是:在建立A类对象的模块——主函数中试图访问A类的保护成员,这是不允许的,因为该成员的访问规则和A类的私有成员是相同的。这就说明在建立A类对象a的模块中无法访问A类的保护成员,在这种情况下,保护成员和私有成员一样得到了很好的隐蔽。

如果类A以公有继承的方式派生产生了B类,则在B类中,A类的保护成员和该类的公有成员一样可以访问。例如:

class A
{
protected:int x;
};
class B:public A
{
public:void fun();
};void B::fun()
{x=5;
}

在派生类B的成员函数fun内部,是完全可以访问基类的保护成员的。

【注意】如果B是A的派生类,B的成员函数只能通过B的对象访问A中定义的protected成员,而不能通过A的对象访问A的protected成员。

相关文章:

C++ 访问控制——公有继承、私有继承、保护继承

派生类继承了基类的全部数据成员和除了构造函数和析构函数之外的全部函数成员&#xff0c;但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员&#xff0c;其访问属性由继承方式控制。 基类的成员有public&#xff08;公有&#xff09;、protected&#xff…...

python性能调试

py-spy生成cpu火焰图 ft5.svg env/xxxx/bin pid26443$env/py-spy record -o /tmp/$f --pid $pid --nativememray实时查看内存 env/xxxx/bin$env/python -m memray run --live --trace-python-allocators --native run_demo.pymemray生成内存火焰图报告 frun_demo_042.bin en…...

738. 单调递增的数字

738. 单调递增的数字 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 示例 1: 输入: n 10 输出: 9示例 2: 输入: n 1234 输出…...

ssh安全远程管理

目录 1、什么是ssh 2、ssh登陆 3、ssh文件传输 1、什么是ssh ssh是 Secure Shell 的缩写&#xff0c;是一个建立在应用层上的安全远程管理协议。ssh 是目前较为可靠的传输协议&#xff0c;专为远程登录会话和其他网络服务提供安全性。利用ssh 协议可以有效防止远程管理过程中…...

外部排序算法总结

一.内排总结 在之前博客里&#xff0c;博主已经介绍了各种内部排序算法的原理和C语言代码实现&#xff0c;不懂的朋友可以在同系列专栏里选择查看&#xff0c;今天介绍常见排序算法的最后一点&#xff0c;也就是外部排序。在此之前&#xff0c;我们先对外部排序的各种算法做一…...

Redis安装以及配置隧道连接(centOs)

目录 1.centOs安装Redis 2. Redis 启动和停⽌ 3. 操作Redis 2.Xshell配置隧道 1.centOs安装Redis #使⽤yum安装Redis yum -y install redis 2. Redis 启动和停⽌ #查看是否启动 ps -ef|grep redis#启动redis: redis-server /etc/redis.conf &#停⽌Redis redis-cli sh…...

mysql二进制方式升级8.0.34

一、概述 mysql8.0.33 存在如下高危漏洞&#xff0c;需要通过升级版本修复漏洞 Oracle MySQL Cluster 安全漏洞(CVE-2023-0361) mysql/8.0.33 Apache Skywalking <8.3 SQL注入漏洞 二、查看mysql版本及安装包信息 [rootlocalhost mysql]# mysql -V mysql Ver 8.0.33 fo…...

Kotlin单例代码实例

目录 一、饿汉式的实现二、懒汉式的实现三、安全 懒汉式的实现四、双重校验DCL 的实现 一、饿汉式的实现 Kotlin版本 object SingletonDemoKt/*** 背后的逻辑代码&#xff1a;public final class SingletonDemoKt {public static final SingletonDemoKt INSTANCE;private Si…...

(7.28-8.3)【大数据新闻速递】《数字孪生工业软件白皮书》、《中国绿色算力发展研究报告》发布;华为ChatGPT要来了

【数字孪生工业软件白皮书&#xff08;2023&#xff09;】 近日&#xff0c;第七届数字孪生与智能制造服务学术会议成功举行&#xff0c;2023《数字孪生工业软件白皮书》在会上正式发布。《白皮书》在《Digital Twin》国际期刊专家顾问委员会指导下&#xff0c;由国家重点研发计…...

TikTok海外抖音云控抢金币宝箱

TikTok海外抖音云控抢金币宝箱 中芯密科云控系统是一个稳定、操作简单的自动化管理工具&#xff0c;专为大型机房设计&#xff0c;可以监控、控制和管理机房内的设备。该系统具有负载均衡、操作简单、高容错等特点&#xff0c;能够提高机房设备的稳定性和可用性。 该系统具有以…...

H3C交换机如何通过MAC和IP查寻对应ARP信息

环境&#xff1a; H3C S6520-26Q-SI version 7.1.070, Release 6326 问题描述&#xff1a; H3C交换机如何通过MAC 查寻对应IP信息 解决方案&#xff1a; 一、已知设备MAC地址为ac11-b134-d066 通过MAC 查寻对应IP信息 命令 dis arp | in X-X-X [H3C]dis arp | in ac11…...

python进阶

目录 Json数据格式 前言 JSON格式 python数据和Json数据的相互转化 多线程 进程和线程 串行和并行 多线程编程 创建线程参数 具体案例 网络编程 套接字 socket服务端编程步骤 socket客户端编程步骤 python操作mysql数据库 查询并接收数据 数据插入 Json数据格…...

spring boot 配置文件和属性注入

文章目录 配置文件位置和路径自定义配置文件 属性注入添加yaml文件的支持 配置文件 位置和路径 当我们创建一个 Spring Boot 工程时&#xff0c;默认 resources 目录下就有一个 application.properties 文件&#xff0c;可以在 application.properties 文件中进行项目配置&am…...

springboot+vue私人健身和教练预约管理系统 nt5mp

随着世界经济信息化、全球网络化的到来&#xff0c;信息线上管理的飞速发展&#xff0c;为私人健身和教练预约管理的改革起到关键作用。若想达到安全、快捷的目的&#xff0c;就需要拥有信息化的组织和管理模式&#xff0c;建立一套合理、畅通、高效的私人健身和教练预约管理系…...

Kotlin基础(十一):反射和注解

前言 本文主要讲解kotlin反射和注解。 Kotlin文章列表 Kotlin文章列表: 点击此处跳转查看 目录 1.1 kotlin反射 1.1.1 kotlin反射概念和常见使用场景 在Kotlin中&#xff0c;反射是一种能够在运行时动态地获取、检查和操作类、属性、方法等结构的能力。Kotlin为反射提供了一…...

DALLE2论文解读及实现(一)

DALLE2: Hierarchical Text-Conditional Image Generation with CLIP Latents paper: https://cdn.openai.com/papers/dall-e-2.pdf github: https://github.com/lucidrains/DALLE2-pytorch DALLE2概览&#xff1a; - CLIP模型&#xff1a; 用于生成text embedding zt 和image …...

RabbitMQ-API

这里写目录标题 Hello word 模式添加依赖生产者消费者获取信道工具类 Work Queues模式消费者代码 C1开启多线程运行启动 消费者代码 C2生产者代码 消息应答自动应答消息应答的方法Multiple 的解释消息自动重新入队消息手动应答代码消费者API 队列持久化消息持久化不公平分发消息…...

外边距实现居中的写法

1、代码实例 2、默认是贴到左侧对齐的&#xff0c;但我们想要把他贴到中间对齐 3、居中的写法 4、这样就可以保证盒子居中了 5、以上写法仅适于行内元素和行内块元素的写法&#xff0c;有没有什么方法适用于行内块元素&#xff1a;可以添加text-align:center进行添加&#xff0…...

剑指 Offer 20. 表示数值的字符串 (正则 逐步分解)

文章目录 题目描述题目分析法一&#xff1a;完整代码: 法二&#xff1a;完整代码: 题目描述 请实现一个函数用来判断字符串是否表示数值&#xff08;包括整数和小数&#xff09;。 数值&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 若干空格 一个 小数 或者…...

【深度学习】Transformer,Self-Attention,Multi-Head Attention

必读文章&#xff1a; https://blog.csdn.net/qq_37541097/article/details/117691873 论文名&#xff1a;Attention Is All You Need 文章目录 1、Self-Attention 自注意力机制2、Multi-Head Attention 1、Self-Attention 自注意力机制 Query&#xff08;Q&#xff09;表示当…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

高防服务器价格高原因分析

高防服务器的价格较高&#xff0c;主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因&#xff1a; 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器&#xff0c;因此…...

在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南

在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析

MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录&#xff0c;这个目录下存放着许多可执行文件。与其他系统的可执行文件类似&#xff0c;这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中&#xff0c;用…...