C++类成员的访问控制

前言

本篇文章介绍C++类成员的访问控制

关键字

C++的类成员通过使用三个关键字来对成员的访问进行控制,并且只有这三个关键字:

  • public
  • private
  • protected

使用场景

上面的三个关键字会扮演两种角色,也就是说会有两种使用的地方:

  • 作为访问说明符
  • 作为派生运算符

上面的名字比较拗口,看一下例子就明白了,下面的例子是作为访问说明符

class Base
{// 这里的public、protected、private都是作为访问说明符
public:int a = 0;
protected:int b = 1;
private:int c = 2;
};

注意: class的默认访问说明符是private,struct的默认访问说明符是public

下面的例子是作为派生运算符

class A0:public Base
{
};class B0:private Base
{
};class C0:protected Base
{
};

无论是访问说明符还是派生运算符,最终的目的都是控制对类成员的访问,既然目的明确了,我们就看一下,访问一个类成员的情况有哪些:

  1. 在当前类访问类的成员
  2. 在派生类中访问基类的成员
  3. 作为用户访问当前类的成员

在当前类访问类的成员

这种情况是最简单的,因为上面提到的三个关键字并不会对这种情况有任何影响。也就是说在当前类中可以访问所有类的成员,不管是private、public还是protected。,看下面的例子:

class Base
{// 这里的public、protected、private都是作为访问说明符
public:int a = 0;void print();
protected:int b = 1;
private:int c = 2;
};void Base::print()
{printf("a = %d, b = %d, c = %d\n",a,b,c);
}

其实关键字的存在并不是为了限制类内对成员的访问,而是为了限制派生类或者使用类对象的用户对类成员的访问

在派生类中访问基类的成员

现在,如果我们创建一个继承基类的派生类,那么,派生类至少得需要确定两件事情:

  1. 派生类中可以访问哪些成员
  2. 派生类中可以访问的成员的访问修饰符是什么

还记得文章开始提到的访问说明符和派生运算符吗?

  • 访问说明符决定当前派生类可以访问基类的哪些成员
  • 访问说明符和派生运算符共同决定当前派生类可以访问的成员的访问修饰符是什么

先看第一条,派生类可以访问基类的public成员和protected成员,实际上你可以简单的认为派生类中包含基类的public成员和protected成员,根据在当前类访问类的成员的原则,我们可以在派生类中访问这些成员。看下面的例子:

class Base
{// 这里的public、protected、private都是作为访问说明符
public:int a = 0;
protected:int b = 1;
private:int c = 2;
};class A0:public Base
{void print();
};
void A0::print()
{printf("a = %d, b = %d, c = %d\n",a,b,c);
}

会报错:

'c' is a private member of 'Base'

但是a和b是可以访问的。
下面再来看第二条,我们既然认为派生了包含了基类的public成员和protected成员,那么这些成员在派生类中是什么访问级别呢?这个得根据派生运算符来决定:

  • 如果派生运算符是private(对于class而言,这是默认值,对于struct,默认是public),则基类的public成员和protected成员的访问级别都是private
  • 如果派生运算符是protected,则基类的public成员的访问级别变成protected,基类的protected成员的访问级别不变。
  • 如果派生运算符是public,则基类的public成员和protected成员的访问级别都不变。

看下面的例子:

#include <iostream>
class Base
{// 这里的public、protected、private都是作为访问说明符
public:int a = 0;
protected:int b = 1;
private:int c = 2;
};class A0:public Base{};
class B0:private Base{};
class C0:protected Base{};
class A00:A0{void print(){printf("a=%d,b=%d",a,b);}};
class B00:B0{void print(){printf("a=%d,b=%d",a,b);}};// 1.这里报错,'a'和'b' is a private member of 'Base'
class C00:C0{void print(){printf("a=%d,b=%d",a,b);}};
int main(int argc, const char * argv[])
{C0 c0;c0.a; // 2. 这里报错'a' is a protected member of 'Base'A0 a0;a0.a; // 3. 没有报错return 0;
}

报错的地方我标记出来了

  • 看报错1,因为B0是通过private派生运算符继承的Base,所以在B0中,a,b都被设置为private的访问限制,所以继承的B00中不能使用a和b
  • 看报错2,因为C0是通过protected派生运算符继承的Base,所以在C0中,a,b都被设置为protected的访问限制,虽然继承的C00中可以使用a和b,但是如果作为用户使用C0类,还是不能访问a和b。
  • 看3为什么没有报错,因为A0是通过public派生运算符继承的Base,所以在A0中,a被设置为public,b依然保持protected,所以对于使用A0的用户来说,依然可以访问a。

作为用户访问当前类的成员

通过上一节我们把基类的成员引入派生类并通过派生运算符来标记访问权限以后,作为用户访问类的成员规则已经很清楚了,因为对于用户来说,只能访问类的public成员,而不能访问protected和private成员,但是依然有几种情况需要我们注意:

  • 如果用户正好就是当前类呢
  • 如果用户是当前类的派生类呢
  • 用户不能是当前类的基类,可以想想为什么?

看下面的例子:

#include <iostream>
class Base
{// 这里的public、protected、private都是作为访问说明符
public:int a = 0;
protected:int b = 1;
private:int c = 2;
};class A0:public Base
{void print1(Base& val){// 报错:'b' is a protected member of 'Base'printf("a=%d,b=%d",val.a,val.b);}void print2(A0& val){printf("a=%d,b=%d",val.a,val.b);}
};

我们在A0中访问Base对象的protected成员报错
但是我们在A0中访问A0对象的protected成员就没事

先来看为什么在A0中访问A0对象的protected成员就没事,要分析这个问题,先得明白一件事儿,那就是:
类函数不是类对象的一部分,也就是类函数是所有类对象共用的
既然这样,我们为了能在函数中访问类的成员,编译器向类函数添加了一个参数this,请注意,对于A0类来说,这个this正是一个A0对象。既然this能访问自己的protected成员,A0对象也是可以的,不仅如此,在A0类函数中,A0对象也可以访问自己的private成员,看下面例子:

#include <iostream>
class Base
{// 这里的public、protected、private都是作为访问说明符
public:int a = 0;
protected:int b = 1;
private:int c = 2;
};class A0:public Base
{
private:int aa=0;void print2(A0& val){printf("a=%d,b=%d",val.aa,val.b);}
};

这个执行就没问题

然后看为什么在A0中访问Base对象的protected成员报错,因为如果这种情况允许的话,Base对象的protected成员对用户来说就没有意义了,我可以随便定义一个继承Base的类,然后在类函数中可以随便修改Base对象的protected成员值。

用比较正式的语言总结一下:
对于派生类来说,不能在派生类中访问一个基类对象的受保护成员,但是可以通过访问一个当前派生类的对象来访问基类的受保护成员

如何改变成员的可访问性

我们可以使用using声明改变派生类成员的可访问性,看下面的例子:

#include <iostream>
class Base
{// 这里的public、protected、private都是作为访问说明符
public:int a = 0;
protected:int b = 1;
private:int c = 2;
};
class A0:private Base
{
public:using Base::a;using Base::b;
protected:
private:int aa=0;void print2(A0& val){printf("a=%d,b=%d",val.aa,val.b);}
};class A00:A0
{void print(){printf("a=%d,b=%d\n",a,b);}
};int main(int argc, const char * argv[])
{A0 a0;a0.b;return 0;
}

在A0类中,我们通过使用

using Base::a;
using Base::b;

使a和b变成了A0的public成员,在使用using声明的时候需要注意:

  • using声明的成员必须是可以访问的,例如不能将基类的private成员变成public
  • using声明所在的访问说明符就是声明成员修改的访问级别。

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

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

相关文章

【数值分析】插值法,lagrange插值,牛顿插值

1. 插值法介绍 插值法是一种通过已知数据点来估计未知数据点的方法。它通过构建一个函数或曲线&#xff0c;使其经过已知数据点&#xff0c;从而在数据点之间进行估计或预测。插值法的基本思想是假设已知数据点之间存在某种规律或趋势&#xff0c;并利用这种规律来推断未知数据…

全解析阿里云Alibaba Cloud Linux镜像操作系统

Alibaba Cloud Linux是基于龙蜥社区OpenAnolis龙蜥操作系统Anolis OS的阿里云发行版&#xff0c;针对阿里云服务器ECS做了大量深度优化&#xff0c;Alibaba Cloud Linux由阿里云官方免费提供长期支持和维护LTS&#xff0c;Alibaba Cloud Linux完全兼容CentOS/RHEL生态和操作方式…

conda环境下Could not create share link解决方法

1 问题描述 在运行chatglm-6B项目时&#xff0c;运行python web_demo.py&#xff0c;出现如下错误&#xff1a; (chatglm) [rootlocalhost ChatGLM2-6B]# python web_demo.py Loading checkpoint shards: 100%|██████████████████████████████…

SwiftUI之深入解析如何创建一个灵活的选择器

一、前言 在 Dribbble 上找到的设计的 SwiftUI 实现时&#xff0c;可以尝试通过一些酷炫的筛选器扩展该项目以缩小结果列表。筛选视图将由两个独立的筛选选项组成&#xff0c;两者都有一些可选项可供选择。但是&#xff0c;在使用 UIKit 时&#xff0c;总是将这种类型的视图实…

Anaconda下调用ArcGIS的arcpy工具包

1.从64位切换到32位开发模式: set CONDA_FORCE_32BIT1 2. 创建2.7的环境 conda create -n py27 python2.7 3.将ArcGIS的目录C:\Python27\ArcGIS10.1\Lib\site-packages\Desktop10.1.pth直接拷贝到Anaconda3的目录里&#xff08;C:\Users\xxx\Anaconda3\Lib\site-packages&a…

RK3568 学习笔记 : ubuntu 20.04 下 Linux-SDK 镜像烧写

前言 开发板&#xff1a;【正点原子】ATK-DLRK3568 开发板&#xff0c;编译完 Linux-SDK 后&#xff0c;生成了相关的镜像文件&#xff0c;本篇记录一下 镜像烧写&#xff0c;当前编译环境在 VMware 虚拟机中&#xff0c;虚拟机系统是 ubuntu 20.04 此次烧写还算顺利&#xff…

Callback Hook

一、Callback Hook 函数名&#xff1a;useCallback 用于得到一个固定引用值的函数&#xff0c;通常用它进行性能优化。 useCallback: 该函数只需要传入两个参数&#xff1a;一个回调函数和一个依赖数组即可。 1.函数&#xff0c;useCallback会固定该函数的引用&#xff0c;…

[USACO04OPEN] The Cow Lineup

题目描述 约翰的 N &#xff08; 1 ≤ N ≤ 100000 &#xff09; N &#xff08; 1 \leq N \leq 100000 &#xff09; N&#xff08;1≤N≤100000&#xff09; 只奶牛站成了一列。每只奶牛都写有一个号牌&#xff0c;表示她的品种&#xff0c;号牌上的号码在 1 … K &#x…

【C++】知识点汇总(上)

C知识点复习上 一、C 概述1. 基本数据类型2. 变量定义和访问3. 常量与约束访问 二、程序控制结构详解与示例1. 表达式2. 选择控制2.1 if 语句2.2 switch 语句 3. 循环控制3.1 for 循环3.2 while 循环3.3 do-while 循环 4. goto 语句5. 控制语句的嵌套 三、函数1. 函数的定义和调…

搜索二维矩阵 II(LeetCode 240)

1.问题描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10…

快速、准确地检测和分类病毒序列分析工具 ViralCC的介绍和详细使用方法,fudai shiyong ijaoben

介绍 viralcc是一个基因组病毒分析工具&#xff0c;可以用于快速、准确地检测和分类病毒序列。 github&#xff1a;dyxstat/ViralCC: ViralCC: leveraging metagenomic proximity-ligation to retrieve complete viral genomes (github.com) Instruction of reproducing resul…

ubuntu的boot分区被删除恢复

在鼓捣黑苹果的时候&#xff0c;误删了ubuntu的boot分区&#xff0c;进系统的时候出现emergency mode&#xff0c;那么现在来讲讲怎么恢复 首先做一个ubuntu的启动盘&#xff0c;然后进入启动盘的系统选择试用 呼出命令行&#xff0c;然后添加一个源 sudo add-apt-repository…

207.【2023年华为OD机试真题(C卷)】小朋友来自多少小区(贪心算法实现-JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-小朋友来自多少小区二.解题思路三.题解代码Pyt…

微众区块链观察节点的架构和原理 | 科普时间

践行区块链公共精神&#xff0c;实现更好的公众开放与监督&#xff01;2023年12月&#xff0c;微众区块链观察节点正式面向公众开放接入功能。从开放日起&#xff0c;陆续有多个观察节点在各地运行&#xff0c;同步区块链数据&#xff0c;运行区块链浏览器观察检视数据&#xf…

flutter项目用vscode打包apk包,完美运行到手机上

1.创建密钥库 执行以下命令: keytool -genkey -v -keystore F:/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key 生成 2,048 位RSA密钥对和自签名证书 (SHA256withRSA) (有效期为 10,000 天) 2.填写密钥内容 执行以上命令后会提示一次输入密钥库密码、确认…

【ES6语法学习】解构赋值

文章目录 引言一、什么是解构赋值1.1什么是解构赋值1.2 数组的解构赋值1.2.1 基本用法1.2.2 默认值1.2.3 剩余参数 1.3 对象的解构赋值1.3.1 基本用法1.3.2 默认值1.3.2 剩余参数 1.4 字符串的解构赋值1.5 函数参数的解构赋值 二、解构赋值的优势和应用场景2.1 代码简化和可读性…

【React系列】JSX核心语法和原理

本文来自#React系列教程&#xff1a;https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. ES6 的 class 虽然目前React开发模式中更加流行hooks&#xff0c;但是依然有很多的项目依然是使用类组件&#x…

es索引数据过滤查询

1.我们往kibana插入数据,来进行查询 POST /t1/_doc/ {"name":"cat","age":"18","address":"BJ","job":"dev" } POST /t1/_doc/ {"name":"dog","age":"1…

改善 GitHub Pages 阅读体验:Quick Docs

一个不到 10MB 的小工具&#xff0c;来提供本地、快速的文档访问&#xff0c;来改善开发过程中&#xff0c;阅读在线文档体验糟糕的问题。 以及&#xff0c;介绍如何快速制作一个利于分发使用的&#xff0c;离线文档工具包。 写在前面 即使现在 AI 辅助编码和 Chat Bot 类的…

【git使用】了解三种git commit合并的使用场景(rebase、merge、cherry-pick)

参考 【Git学习笔记】逃不掉的merge和rebase-腾讯云开发者社区-腾讯云git merge 和 git rebase - 知乎git cherry-pick 教程 - 阮一峰的网络日志 简单理解各种合并的方法 线性合并&#xff0c;使用 rebase —— feature 分支开发&#xff0c;提交前拉取 master 最新改动进行…