C++ 类的静态成员
在结构化程序设计中程序模块的基本单位是函数,因此模块间对内存中数据的共享是通过函数与和函数之间的数据共享来实现的,其中包括两个途径——参数传递和全局变量。
面向对象的程序设计方法兼顾数据的共享和保护,将数据与操作数据的函数封装在一起,构成集成度更高的模块。类中的数据成员可以被同一类中的任何一个函数访问。这样一方面在类内部的函数之间实现了数据的共享,另一方面这种共享是受限制的,可以设置适当的访问控制属性。把共享只限制在类的范围之内,对类外来说,类的数据成员仍是隐藏的,达到了共享与安全两全。
然而这些还不是数据共享的全部。对象与对象之间也需要共享数据。
类的静态成员是解决同一个类中不同对象之间数据和函数共享问题的。
例如可以抽象出某公司全体雇员的共性,设计如下雇员类:
class Employee
{int empNo;int id;string name;...//其他数据成员与函数成员略
};
如果需要统计雇员总数,这个数据存放在什么地方呢?若以类外的变量来存储总数,不能实现数据的隐藏。若在类中增加一个数据成员来存放总数,必然在每一个对象中都存储一个副本,不仅冗余,而且每个对象分别维护一个“总数”,容易造成数据的不一致性。由于这个数据应该为Employee类的所有对象所共享的,比较理想的方案是类的所有对象共同拥有一个用来存放总数的数据成员,即静态数据成员。
1.静态数据成员
我们说“一个类的所有对象具有相同的属性”,是指属性的个数、名称、数据类型相同,各个对象的属性值则可以各不相同,这样的属性在面向对象方法中,称为“实例属性”,在C++程序中以类的非静态数据成员表示。例如上述的Employee类中的empNo,id,name都是以非静态数据成员表示的实例属性,它们在类的每一个对象中都拥有一个副本,这样的实例属性正是每个对象区别其他对象的特征。
如果某个属性为整个类所共有,不属于任何一个具体对象,则采用static关键字来声明为静态成员。静态成员在每个类只有一个副本,由该类的所有对象共同维护和使用,从而实现了同一类的不同对象之间的数据共享。
**类属性是描述类的所有对象共同特征的一个数据项,对于任何对象实例,它的属性值是相同的。**简单地说,如果将“类”比作一个工厂,对象是工厂生产出的产品,那么静态成员是存放在工厂中、属于工厂的,而不属于每个产品。
**静态数据成员具有静态生存期。**由于静态数据成员不属于任何一个对象,因此可以通过类名对它进行访问,一般的用法是类名::标识符。在类的定义中仅仅对静态数据成员进行引用性声明,必须在命名空间作用域的某个地方使用类名限定定义性声明,这时也可以进行初始化。在UML语言中,静态数据成员通过在数据成员下方添加下划线来表示。
【注意】之所以类的静态成员需要在类定义之外再加以定义,是因为需要以这种方式专门为它们分配空间。非静态数据成员无须以这种方式定义,因为它们的空间是与它们所属对象的空间同时分配的。
【例】具有静态数据成员的Point类,静态数据成员count用于统计Point类的对象个数。
#include<iostream>
using namespace std;class Point//Point类的定义
{
public://外部接口Point(int x = 0, int y = 0) :x(x), y(y)//构造函数{//在构造函数中对count累加,所有对象共同维护同一个countcount++;}Point(Point& p)//拷贝构造函数{x = p.x;y = p.y;count++;}~Point(){count--;}int getX(){return x;}int getY(){return y;}void ShowCount()//输出静态数据成员{cout << " 对象数量:" << count << endl;}
private://私有成员int x, y;static int count;//静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定int main()//主函数
{Point a(4, 5);//定义对象a,其构造函数会使count加一cout << "Point A:" << "(" << a.getX() << ", " << a.getY() << ")";a.ShowCount();//输出对象的个数Point b(a);//定义对象b,其构造函数会使count加一cout << "Point B:" << "(" << b.getX() << ", " << b.getY() << ")";b.ShowCount();//输出对象的个数return 0;
}
运行结果及分析:
上例中,类Point的数据成员count被声明为静态,用来给Point类的对象计数,每定义一个新对象,count的值就相应加1。静态数据成员count的定义和初始化在类外进行,初始化时引用的方式也值得注意,首先应该注意的是要利用类名来引用,其次,虽然这个静态数据成员是私有类型,在这里却可以直接初始化。除了这种特殊场合,在其他地方,例如主函数中就不允许直接访问。count的值是在类的构造函数中计算的,a对象生成时,调用有默认参数的构造函数,b对象生成时,调用拷贝构造函数,两次调用构造函数都访问的是同一个静态成员count。通过对象a和对象b分别调用ShowCount函数,输出的也是同一个count在不同时刻的数值。这样就实现了a,b两个对象之间的数据共享。
【注意】在对类的静态私有数据成员初始化的同时,还可以引用类的其他私有成员。例如,如果一个类T存在类型为T的静态私有对象时,那么可以用该类的私有构造函数将其初始化。

2.静态函数成员
在上例中,函数ShowCount是专门用来输出静态成员count的。要输出count只能通过Point类的某个对象来调用函数ShowCount。在所有对象声明之前count的值是初始值0。如何输出这个初始值呢?显然由于尚未声明如何对象,无法通过对象来调用ShowCount。由于count是为整个类所共有的,不属于任何对象,因此我们自然会希望对count的访问也不要通过整个对象。将程序代码进行改写如下:
#include<iostream>
using namespace std;class Point//Point类的定义
{
public://外部接口Point(int x = 0, int y = 0) :x(x), y(y)//构造函数{//在构造函数中对count累加,所有对象共同维护同一个countcount++;}Point(Point& p)//拷贝构造函数{x = p.x;y = p.y;count++;}~Point(){count--;}int getX(){return x;}int getY(){return y;}void ShowCount()//输出静态数据成员{cout << " 对象数量:" << count << endl;}
private://私有成员int x, y;static int count;//静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定int main()//主函数
{Point::ShowCount();//直接通过类名调用函数,输出对象个数的初始值Point a(4, 5);//定义对象a,其构造函数会使count加一cout << "Point A:" << "(" << a.getX() << ", " << a.getY() << ")";a.ShowCount();//输出对象的个数Point b(a);//定义对象b,其构造函数会使count加一cout << "Point B:" << "(" << b.getX() << ", " << b.getY() << ")";b.ShowCount();//输出对象的个数return 0;
}
在主函数中,加入语句Point::ShowCount();直接通过类名调用函数,输出对象个数的初始值,但是编译会出错,因为对普通函数成员的调用必须通过对象名。
尽管如此C++中还是可以有办法实现我们上述期望,这就是通过使用静态成员函数。
**所谓静态成员函数就是使用static关键字声明的函数成员。**和静态数据成员一样,静态成员函数也属于整个类,由于同一个类的所有对象共同拥有,为这些对象所共享。
静态成员函数可以通过类名或者对象名两种方式调用,而非静态成员函数只能通过对象名来调用。
【注意】虽然静态函数成员可以通过类名或者对象名两种方式调用,,但一般习惯通过类名调用。因为即使通过对象名调用,起作用的也只是对象的类型信息,与所使用的对象毫无关系。
**静态成员函数可以通过类名可以直接访问该类的静态数据和函数成员。而访问非静态成员,必须通过对象名。**例如:
class A
{
public:static void f(A a);
private:int x;
};
void A::f(A a)
{cout << x;//error 对x的引用错误cout << a.x;//正确
}
可以看到,通过静态函数成员访问非静态成员相当麻烦,一般情况下,静态函数成员主要用来访问同一个类中的静态数据成员,维护对象之间共享的数据。
【注意】之所以在静态成员函数中访问类的非静态成员需要指明对象,是因为静态成员函数对静态成员函数的调用是没有目的对象的,因此不能像非静态成员函数那样,隐含地通过目的对象访问类的非静态成员。
【例】具有静态数据和静态函数成员的Point类
class Point//Point类的定义
{
public://外部接口Point(int x = 0, int y = 0) :x(x), y(y)//构造函数{//在构造函数中对count累加,所有对象共同维护同一个countcount++;}Point(Point& p)//拷贝构造函数{x = p.x;y = p.y;count++;}~Point(){count--;}int getX(){return x;}int getY(){return y;}static void ShowCount()//静态函数成员{cout << " 对象数量:" << count << endl;}
private://私有成员int x, y;static int count;//静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定int main()//主函数
{Point a(4, 5);//定义对象a,其构造函数会使count加一cout << "Point A:" << "(" << a.getX() << ", " << a.getY() << ")";Point::ShowCount();//直接通过类名调用函数,输出对象的个数Point b(a);//定义对象b,其构造函数会使count加一cout << "Point B:" << "(" << b.getX() << ", " << b.getY() << ")";Point::ShowCount();//直接通过类名调用函数,输出对象的个数return 0;
}
运行结果及分析:
与第一个例子相比,这里只是在类的定义中,将ShowCount函数改写为静态成员函数。于是在主函数中既可以使用类名也可以使用对象名来调用ShowCount。

这个程序与第一个例子的输出结果完全相同。相比而言,采用静态函数成员的好处就是可以不依赖任何对象,直接访问静态数据。
相关文章:
C++ 类的静态成员
在结构化程序设计中程序模块的基本单位是函数,因此模块间对内存中数据的共享是通过函数与和函数之间的数据共享来实现的,其中包括两个途径——参数传递和全局变量。 面向对象的程序设计方法兼顾数据的共享和保护,将数据与操作数据的函数封装…...
360T7路由器进行WiFi无线中继教程
360T7路由器进行WiFi中继教程 1. 概述2. 360T7路由器进行WiFi中继实现教程2.1 登录路由器管理界面2.2 选择上网方式2.3 搜索WiFi2.4 连接WiFi2.5 点击确认2.6 在主页面查看网络 1. 概述 中继路由系统由一组中继路由器组成,为不能交换路由信息的路由域提供中继路由。…...
主成分分析
主成分分析 相关概念方差协方差协方差矩阵特征值和特征向量 主成分分析数据降维主成分分析原理主成分分析过程sklearn库中的PCA主成分分析实现案例 相关概念 方差 方差是一个用来衡量一组数据离散程度的统计量,它是各样本与样本均值的差的平方和的平均值。方差越大…...
笙默考试管理系统-MyExamTest(26)
笙默考试管理系统-MyExamTest(26) 目录 一、 笙默考试管理系统-MyExamTest 二、 笙默考试管理系统-MyExamTest 三、 笙默考试管理系统-MyExamTest 四、 笙默考试管理系统-MyExamTest 五、 笙默考试管理系统-MyExamTest 笙默考试管理系统-MyEx…...
重新理解 RocketMQ Commit Log 存储协议
最近突然感觉:很多软件、硬件在设计上是有 root reason 的,不是 by desgin 如此,而是解决了那时、那个场景的那个需求。一旦了解后,就会感觉在和设计者对话,了解他们的思路,学习他们的方法,思维…...
ES6基础知识十:你是怎么理解ES6中 Decorator 的?使用场景?
一、介绍 Decorator,即装饰器,从名字上很容易让我们联想到装饰者模式 简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。 ES6中Decorator功能亦如此,其本质也不是什么高大…...
接口/Web自动化测试如何做?框架如何搭建封装?
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 自动化测试怎么做…...
Linux怎么从网络上下载文件
wget命令用于从网络上下载文件 下载文件: wget [URL]使用wget命令加上要下载的文件的URL,可以将文件下载到当前目录。 指定保存的文件名: wget -O [保存的文件名] [URL]使用-O选项后跟保存的文件名,可以指定下载的文件保存的名称…...
Flutter携带JSON参数post请求
在Flutter中发送带有JSON参数的网络请求,你可以使用HTTP库(如http或dio)来实现。以下是使用http库发送网络请求并携带JSON参数的示例: import package:http/http.dart as http; import dart:convert;// 创建参数Map Map<Strin…...
【vue】vue-image-lazy图片懒加载使用与介绍【超详细+npm包源代码】
简介 当前插件是基于vue3,写的一个图片懒加载,文章最下方是npm包的源码,你可以自己拿去研究和修改,如有更好的想法可以留言,如果对你有帮助,可以点赞收藏和关注,谢谢。 后续会添加图片放大和切…...
MFC第二十四天 使用GDI对象画笔和画刷来开发控件(分页控件选择态的算法分析、使用CToolTipCtrl开发动静态提示)
文章目录 GDI对象画笔和画刷来开发控件梯形边框的按钮控件CMainDlg.hCMainDlg.cppCLadderCtrl.hCLadderCtrl.cpp 矩形边框的三态按钮控件 CToolTipCtrl开发动静态提示CMainDlg.hCMainDlg.cppCLadderCtrl.hCLadderCtrl.cpp: 实现文件 矩形边框的三态按钮控件 CToolTipCtrl开发动…...
【NLP-新工具】语音转文本与OpenAI的用途
一、说明 OpenAI最近2022发布了一个名为Whisper的新语音识别模型。与DALLE-2和GPT-3不同,Whisper是一个免费的开源模型。它的主要功能就是将语音翻译成文本。本文将介绍如何使用这个重要应用库。 二、 Whisper概念 2.1 Whisper是啥? Whisper 是一种自动…...
try-catch-finally的字节码原理
Java 中有一个非常重要的内容是 try-catch-finally 的执行顺序和返回值问题,其中 finally 一定会执行,但是为什么会这样? 下面看下 try-catch-finally 背后的实现原理 try-catch public class Test {public static void main(String[] args)…...
基于双层优化的微电网系统规划设计方法(Matlab代码实现)
目录 💥1 概述 1.1 微电网系统结构 1.2 微电网系统双层规划设计结构 1.3 双层优化模型 1.4 上层容量优化模型 1.5 下层调度优化模型 📚2 运行结果 🎉3 文献来源 🌈4 Matlab代码、数据、文章讲解 💥1 概述 文献来源&…...
【Nginx13】Nginx学习:HTTP核心模块(十)Types、AIO及其它配置
Nginx学习:HTTP核心模块(十)Types、AIO及其它配置 今天学习的内容也比较简单,主要的是 Types 相关的配置,另外还会了解一下 AIO 以及部分没有特别大的分类归属的配置指令的使用。后面的内容都是 HTTP 核心模块中比较小…...
2023年华数杯数学建模C题思路分析
文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor 1 竞赛信息 为了培养学生的创新意识及运用数…...
安卓手机变身Linux服务器
文章目录 前言一、准备工作1、安卓手机2、下载软件二、开始安装1、检查系统,确认版本并安装2、配置(安卓7.0 及以上的用户忽略此步)3、问题处理【没有异常的小伙伴忽略】总结前言 在实际开发中有很多地方都需要服务器资源,但是服务器资源不论在哪里都是比较紧缺的资源,今…...
【Ansible】Ansible自动化运维工具之playbook剧本
playbook 一、playbook 的概述1. playbook 的概念2. playbook 的构成 二、playbook 的应用1. 安装 httpd 并启动2. 定义、引用变量3. 指定远程主机 sudo 切换用户4. when条件判断5. 迭代6. Templates 模块6.1 添加模板文件6.2 修改主机清单文件6.3 编写 playbook 7. tags 模块 …...
Yolov8目标检测
Yolov8目标检测 目录 Yolov8目标检测一、准备数据集二、源码下载配置2.1 下载库2.2 修改配置2.3 训练2.4 验证2.5 测试2.6 模型导出2.7 本地测试 一、准备数据集 Yolov8只支持yolo格式的数据,所以,需要将数据集格式调整为 datasets|images|train|00000…...
Jmeter用于接口测试中,关联如何实现
Jmeter用于接口测试时,后一个接口经常需要用到前一次接口返回的结果,应该如何获取前一次请求的结果值,应用于后一个接口呢,拿一个登录的例子来说明如何获取。 1、打开jmeter, 使用的3.3的版本,新建一个测试计划&#x…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
