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…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...