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

7_2、C++程序设计进阶:数据共享

数据与函数

  • 数据与函数
    • 局部变量
    • 全局变量
    • 类的数据成员
  • 类的静态成员
    • 静态数据成员
    • 静态函数成员
  • 友元
    • 友元函数
    • 友元类

== 函数之间实现数据共享有以下几种方式:局部变量、全局变量、类的数据成员、类的静态成员和友元。==
如何共享局部变量呢?

  1. 在主调函数和被调函数之间通过参数传递来共享。
  2. 全局变量具有文件作用域,所以作用域中的各个函数都能共享全局变量。
  3. 类的数据成员具有类作用域,能够被类的函数成员共享。
  4. 类的静态成员。
  5. 友元共享。

数据与函数

在结构化程序设计中,有人提出“数据结构+算法=程序设计”,数据结构就是数据的组织,算法是用函数实现的,可见数据和函数很早就被看作程序设计的重点了。面向对象程序设计中,这种观点应稍作一下修改:“数据结构+算法=对象”。就是数据和函数构成了类的对象。
面向对象程序设计中,数据用来描述对象的属性,函数是行为,用来处理数据。将数据和函数封装到一个类里,类中的函数成员可以访问数据成员,函数成员之间可以实现数据共享。

局部变量

局部变量其实一般就是说具有块作用域的变量。如果要在不同的块之间共享存储在局部变量中的数据,只能通过参数传递来实现。这种共享只能在主调函数和被调函数之间进行。因为局部变量具有块作用域,所以不同函数中的局部变量是互不可见的,这也是函数之间的一种数据隐藏,在结构化程序设计中这是实现数据隐藏的唯一方式。而在面向对象设计中主要靠封装来隐藏数据。

全局变量

全局变量具有文件作用域,在整个作用域中,除了定义了同名局部变量的块以外的地方都可以直接访问全局变量。不同的函数在不同的地方都能访问相同的全局变量,这样就实现了函数之间对全局变量的共享。
使用全局变量的优缺点:

  • 优点:在进行软件开发的时候使用全局变量会很方便,不用那么多的传递参数。
  • 缺点:在多处使用了全局变量以后就不清楚此变量在哪些函数中使用过了,除非逐个查找,这样就会带来一个问题,如果全局变量要做一些大的改动,我们就不知道它会影响到哪些函数,这时就要通过大量的测试来排除全局变量变动带来的问题。

结构化程序设计中,只能使用前面提到的局部变量参数传递和全局变量这两种方式共享数据。这两种方式各有利弊,局部变量隐藏性好,但是传递的时候会有很大的系统开销,所以一般只传递少量数据,大量数据通过全局变量的方式共享,但这又使得程序模块间耦合严重,扩展和维护都很困难。C++中可以通过具有文件作用域的全局对象来实现数据和函数的共享,这实际上也属于全局变量的范畴。

类的数据成员

类中封装了数据成员和函数成员。其中数据成员可以被同一个类中的所有函数成员访问,这样就实现了数据的共享。但是我们又可以通过设置访问控制权限,把这种共享限制到类的范围之内,这样类的数据成员对外界来说是隐藏的。也就达到了共享与隐藏的辩证结合。总起来说,要达成这样的效果需要把类的数据成员都设置为私有类型

  • 若将类的数据参数设置为私有,则外部对象不能直接访问该数据对象,只能通过函数接口来访问该私有变量。这样程序模块间的耦合性就大大降低,提高了程序的可扩展性和可维护性,能够大大提高软件开发效率。

类的静态成员

静态成员包括静态数据成员和静态函数成员。

静态数据成员

  1. 使用场景:
    之前讲到的类的数据成员都是一个对象一个拷贝,每个对象都有自己的数据成员的值,但有时候我们需要让某个类的所有对象共享某个数据成员,比如我们有一个学生类CStudent,其中有学号、姓名等数据成员,如果我们想要对学生人数进行统计,那么这个数据应该放在哪里呢?放在类外的某个地方的话数据的隐藏性就不好了。可能有朋友就说了,可以在类中增加一个数据成员存放学生人数,但这样就有两个坏处,第一,该类的每个对象都将有一个这个数据成员的副本,浪费内存;第二,学生人数可能在不同的对象中有不同的数,不一致了。这个时候就需要静态数据成员了。
  2. 特性
    静态数据成员存放的是类的所有对象的某个共同特征的数据,对于每个对象而言,该数据都是相同的,在内存中只存在一份。这与类的一般数据成员不同,一般数据成员会在每个对象中都有一个拷贝,每个拷贝的值可能都不一样。静态数据成员由类的所有对象共同维护和使用,这样就实现了类的对象间的数据共享。
  3. 使用
    声明静态数据成员的方式与一般数据成员不同的是,其前面要加static关键字,比如static int x;。静态数据成员具有静态生存期。它可以通过类名或对象名访问。用类名直接访问的方式为:类名::标识符。在类的声明中仅对静态数据成员进行引用性说明,就是带static的声明,必须在其文件作用域的其他地方进行定义性说明,也可以进行初始化,如果不显式初始化的话会将此静态数据成员初始化为0。
#include <iostream>
using namespace std;
class CStudent         // 学生类的声明
{
public: CStudent(int nID)    { m_nID = nID; m_nCount++; }       // 构造函数CStudent(CStudent &s);                                 // 拷贝构造函数int GetID(){ return m_nID;}void GetCount()       { cout<<" 学生人数:"<<m_nCount<<endl; } // 输出静态数据成员
private: int m_nID;static int m_nCount;                           // 静态数据成员的引用性说明
};
CStudent::CStudent(CStudent &s)
{ m_nID = s.m_nID;m_nCount ++;
}int CStudent::m_nCount=0;                      // 静态数据成员的定义性说明及初始化
int main()
{CStudent A(6);             // 定义对象Acout<<"学生A,"<<A.GetID();A.GetCount();               // 输出此时学生个数CStudent B(A);            // 定义对象B,并用A初始化Bcout<<"学生B,"<<B.GetID(); // 输出此时学生个数B.GetCount();return 0;
}
  • 若将m_nCount的属性改为public,将可以通过CStudent::m_nCount直接访问。
  • 类的使用中一般情况,将数据定义为私有。

静态函数成员

类的私有静态变量无法用限位符来直接调用,此时就采用静态函数来解决。
静态成员函数跟静态数据成员一样,也是由类的所有对象所共有,由他们共同维护和使用。声明时前面也要加static关键字,比如,static void fun(void);。我们可以通过类名或对象名调用公有类型的静态成员函数,而非静态成员函数只能由对象名调用。
静态成员函数可以访问该类的静态数据成员和其他静态成员函数,如果要访问非静态数据成员,则必须将对象作为参数传递进去,然后通过传进去的对象名来访问。

class A
{
public:static void f(A a);
private:int x;
};
void A::f(A a)
{cout<<x;     //对x的引用是错误的cout<<a.x;  //正确
}
  • 所以静态成员函数访问非静态成员很麻烦,它一般用来访问全局变量和同一个类中的静态数据成员。
#include <iostream>
using namespace std;
class CStudent         // 学生类的声明
{
public: CStudent(int nID)    { m_nID = nID; m_nCount++; }       // 构造函数CStudent(CStudent &s);                                 // 拷贝构造函数int GetID()      { return m_nID;}static void GetCount() { cout<<" 学生人数:"<<m_nCount<<endl; } // 输出静态数据成员
private: int m_nID;static int m_nCount;                           // 静态数据成员的引用性说明
};
CStudent::CStudent(CStudent &s)
{ m_nID = s.m_nID;m_nCount ++;
}int CStudent::m_nCount=0;                      // 静态数据成员的定义性说明及初始化
int main()
{CStudent A(6);             // 定义对象Acout<<"学生A,"<<A.GetID();A.GetCount();               // 输出此时学生个数CStudent B(A);            // 定义对象B,并用A初始化Bcout<<"学生B,"<<B.GetID(); // 输出此时学生个数CStudent::GetCount();     //直接通过类名和限定符来访问静态函数return 0;
}

友元

友元是一种破坏类的封装性获取类的隐藏数据的方式。
友元机制:B类的成员函数直接访问A的数据成员。

 class A
{
public:int Getx()   { return x; }
private:int x;
};
class B
{
public:void Set(int y);
private:A a;
};//不正确用法
void B::Set(int y)
{a.x = y;
}

通过友元的方式,某个普通函数或者类的成员函数可以访问某个类中的私有数据,这样就等于在类的封装的很好的外衣上剪开了一个小洞,外界可以通过这个小洞访问到一些类内部的数据。友元提供了一种不同类或对象的成员函数之间、类的成员函数与普通函数之间共享数据的机制。它破坏了类的封装性和类数据的隐藏性,但是又给我们进行软件开发提供了很多方便,在我们实地进行软件开发的时候可以自己在共享和封装之间平衡一下,决定是否选择使用友元。原则上尽量少使用或不使用友元,除非确实很大的提高了开发效率。

  • 在一个类中声明友元的方式是,用关键字friend把普通函数、其他类的成员函数或其他类声明为此类的友元,用friend声明的元素就可以访问此类的私有数据。如果友元是普通函数或是类的成员函数就叫做友元函数,如果友元是一个类则叫做友元类,友元类的所有成员函数均为友元函数。
  • 友元的声明位置为想使用的资源的范围内。

友元函数

友元函数是在类的声明中用关键字friend修饰的普通函数或者其他类的成员函数。友元函数虽不是本类的成员函数,但在它的函数体中可以访问该类对象的私有成员和保护成员。

#include <iostream>
#include <math.h>
using namespace std;
class Data //Data类声明
{ 
public:      //外部接口Data(int xx=0) { x=xx; }int GetX() { return x; }friend int Add(Data &a, Data &b);
private: //私有数据成员int x;
};
int Add(Data &a, Data &b)
{return a.x + b.x;
}
int main()
{Data a(1);Data b(2);int sum = Add(a, b);cout<<"The sum is "<<sum<<endl;return 0;
}
  • 在Data类中声明友元函数Add时只给出了友元函数原型,友元函数Add的实现在类Data外。我们看到,在Add函数体中直接通过对象名访问了Data类的私有成员x,这就是友元的强大作用。我们在类外用一般方式访问x的话必须通过公共接口GetX来实现,若要访问的私有成员很多或者要访问的地方很多就要多次调用函数,对于我们写代码和程序运行都有效率上的损失,但是用友元也有个很大的缺点就是,如果私有数据x发生结构性的变化,那么友元函数就要做相应的修改,所有调用友元函数的地方可能也要修改,这就降低了开发效率,所以是否要使用友元可以自己权衡。

友元类

类也可以声明为另一个类的友元,就像友元函数那样,这个作为另一个类的友元的类就叫做友元类。如果一个类B是类A的友元类,则类B的所有成员函数都是类A的友元函数,都能访问类A的私有成员和保护成员。

class A
{...friend class B;   // 将类B声明为类A的友元类...
}
class A
{  
public:void Display()   { cout<<x<<endl;}friend class B;
private:int x;
}
class B
{  
public:void Set(int i);void Display();
private:A a;
};
void B::Set(int i)
{a.x=i;         // 因为类B是类A的友元类,所以类B的成员函数可以访问类A对象的私有成员
}
void B::Display()
{a.Display();
}

友元类特性:

  1. ***友元关系不能传递,***如果类B是类A的友元,类C又是类B的友元,类C和类A如果没有声明则没有友元关系。
  2. 友元关系是单向的,如果类B是类A的友元,类B的成员函数可以访问类A对象的私有成员和保护成员,但是类A的成员函数不能访问类B对象的私有成员和保护成员。

相关文章:

7_2、C++程序设计进阶:数据共享

数据与函数 数据与函数局部变量全局变量类的数据成员 类的静态成员静态数据成员静态函数成员 友元友元函数友元类 函数之间实现数据共享有以下几种方式&#xff1a;局部变量、全局变量、类的数据成员、类的静态成员和友元。 如何共享局部变量呢&#xff1f; 在主调函数和被调…...

d2-crud-plus 使用小技巧(五)—— 搜索时间(或下拉列表)后,点击X清除按钮后返回值为null,导致异常

问题 使用vue2elementUId2-crud-plus&#xff0c;时间组件自动清除按钮&#xff0c;点击清除按钮后对应的值被设置为null&#xff0c;原本应该是空数组&#xff08;[]&#xff09;&#xff0c;导致数据传到后端后报错。不仅适用于搜索&#xff0c;表单一样有效果。 解决方法 …...

ChatGPT成知名度最高生成式AI产品,使用频率却不高

5月29日&#xff0c;牛津大学、路透社新闻研究所联合发布了一份生成式AI&#xff08;AIGC&#xff09;调查报告。 在今年3月28日—4月30日对美国、英国、法国、日本、丹麦和阿根廷的大约12,217人进行了调查&#xff0c;深度调研他们对生成式AI产品的应用情况。 结果显示&…...

R19 NR移动性增强概况

随着5G/5G-A技术不断发展和业务需求的持续增强&#xff0c;未来网络的部署将不断向高频演进。高频小区的覆盖范围小&#xff0c;用户将面临更为频繁的小区选择、重选、切换等移动性过程。 为了提升网络移动性能和保障用户体验&#xff0c;移动性增强一直是3GPP的热点课题。从NR…...

C语言:如何写文档注释、内嵌注释、行块注释?

技术答疑流程 扫描二维码&#xff0c;添加个人微信&#xff1b;支付一半费用&#xff0c;获取答案&#xff1b;如果满意&#xff0c;则支付另一半费用&#xff1b; 知识点费用&#xff1a;10元 项目费用&#xff1a;如果有项目任务外包需求&#xff0c;可以微信私聊...

Turtle中circle用法详解

在Python的Turtle图形库中&#xff0c;circle方法是一个非常灵活的工具&#xff0c;它允许我们以简单的方式绘制圆或圆的一部分。本文将深入探讨circle方法&#xff0c;特别关注radius和extent参数的用途及其正负值的意义。 一、circle方法概览 首先&#xff0c;让我们了解一…...

stack和queue(1)

一、stack的简单介绍和使用 1.1 stack的介绍 1.stack是一种容器适配器&#xff0c;专门用在具有先进后出&#xff0c;后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入和弹出操作。 2.stack是作为容器适配器被实现的&#xff0c;容器适配器即是…...

前端3剑客(第1篇)-初识HTML

100编程书屋_孔夫子旧书网 当今主流的技术中&#xff0c;可以分为前端和后端两个门类。 前端&#xff1a;简单的理解就是和用户打交道 后端&#xff1a;主要用于组织数据 而前端就Web开发方向来说&#xff0c; 分为三门语言&#xff0c; HTML、CSS、JavaScript 语言作用HT…...

植被变化趋势线性回归以及可视化

目录 植被变化线性回归ee.Reducer.linearFit().reduce()案例:天水市2004-2023年EVI线性回归趋势在该图中,使用了从红色到蓝色的渐变来表示负趋势到正趋势。红色代表在某段时间中,植被覆盖减少,绿色表示持平,蓝色表示植被覆盖增加。 植被变化线性回归 该部分参考Google…...

大话设计模式学习笔记

目录 工厂模式策略模式备忘录模式&#xff08;快照模式&#xff09;代理模式单例模式迭代器模式访问者模式观察者模式解释器模式命令模式模板方法模式桥接模式适配器模式外观模式享元模式原型模式责任链模式中介者模式装饰模式状态模式 工厂模式 策略模式 核心&#xff1a;封装…...

MiniMax公司介绍

MiniMax是一家专注于通用人工智能技术的科技公司&#xff0c;成立于2021年12月。公司致力于成为通用人工智能时代基础设施建设者和内容应用创造者&#xff0c;积极投身于中国人工智能技术高速发展的时代大潮。MiniMax的团队由多位在人工智能领域有着丰富经验的专家组成&#xf…...

lucene 9.10向量检索基本用法

Lucene 9.10 中的 KnnFloatVectorQuery 是用来执行最近邻&#xff08;k-Nearest Neighbors&#xff0c;kNN&#xff09;搜索的查询类&#xff0c;它可以在一个字段中搜索与目标向量最相似的k个向量。以下是 KnnFloatVectorQuery 的基本用法和代码示例。 1. 索引向量字段 首先…...

【2023百度之星初赛】跑步,夏日漫步,糖果促销,第五维度,公园,新材料,星际航行,蛋糕划分

目录 题目&#xff1a;跑步 思路&#xff1a; 题目&#xff1a;夏日漫步 思路&#xff1a; 题目&#xff1a;糖果促销 思路&#xff1a; 题目&#xff1a;第五维度 思路&#xff1a; 题目&#xff1a;公园 思路&#xff1a; 新材料 思路&#xff1a; 星际航行 思路…...

vs2019 QT UI 添加新成员或者控件代码不提示问题解决方法

右键点击头文件&#xff0c;添加ui的头文件 添加现有项 找到uic目录的头文件 打开ui,QtWidgetsApplication2.ui,进行测试 修改一个名字&#xff1a; 重点&#xff1a; 设置一个布局&#xff1a; 点击生成解决方案&#xff1a; 以后每次添加控件后&#xff0c;记得点击保存 这样…...

【面试八股总结】MySQL事务:事务特性、事务并行、事务的隔离级别

参考资料&#xff1a;小林coding 一、事务的特性ACID 原子性&#xff08;Atomicity&#xff09; 一个事务是一个不可分割的工作单位&#xff0c;事务中的所有操作&#xff0c;要么全部完成&#xff0c;要么全部不完成&#xff0c;不会结束在中间某个环节。原子性是通过 undo …...

STL用法总结

文章目录 vector构造常用函数遍历适用情形注意事项使用迭代器删除可能会出现的错误 Set & MultiSet&#xff08;不能用sort,会自动排序&#xff09;构造常用函数删除&#xff0c;查找遍历 unordered_set(不排序集合&#xff09;&#xff0c;unordered_multiset Map & M…...

他人项目二次开发——慎接

接了一个朋友的项目——开发及运营迭代差不多2年多了&#xff0c;整体样子移动端和PC都能正常使用&#xff0c;但后期的扩展性及新功能添加出现瓶颈。 因此给了一部分钱&#xff0c;让我接手来开发——重构架构。 背景说明 朋友公司的技术人员是我帮忙招聘的&#xff0c;相关技…...

k8s之PV、PVC

文章目录 k8s之PV、PVC一、存储卷1、存储卷定义2、存储卷的作用2.1 数据持久化2.2 数据共享2.3 解耦2.4 灵活性 3、存储卷的分类3.1 emptyDir存储卷3.1.1 定义3.1.2 特点3.1.3 用途3.1.4 示例 3.2 hostPath存储卷3.2.1 定义3.2.2 特点3.2.3 用途3.2.4 示例 3.3 NFS存储卷3.3.1 …...

新人学习笔记之(JavaScript作用域)

一、作用域 1.通常来说&#xff0c;一段程序代码中所用的名字并不总是有效和可用的&#xff0c;而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性&#xff0c;增强了程序的可靠性&#xff0c;减少了名字冲突 二、变量的作用域 1.变…...

图论第一天

在单位摸鱼&#xff0c;地铁上看了个开始&#xff0c;图论开了个头&#xff0c;后面也希望能往这个方向上转&#xff0c;努努力吧。 一周没做题啦&#xff0c;后面坚持继续做题&#xff0b;二刷&#xff0c;接着记录每一天&#xff01;&#xff01;&#xff01;加油&#xff0…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...