C++ 类的静态成员

news2024/9/20 18:00:12

在结构化程序设计中程序模块的基本单位是函数,因此模块间对内存中数据的共享是通过函数与和函数之间的数据共享来实现的,其中包括两个途径——参数传递和全局变量。

面向对象的程序设计方法兼顾数据的共享和保护,将数据与操作数据的函数封装在一起,构成集成度更高的模块。类中的数据成员可以被同一类中的任何一个函数访问。这样一方面在类内部的函数之间实现了数据的共享,另一方面这种共享是受限制的,可以设置适当的访问控制属性。把共享只限制在类的范围之内,对类外来说,类的数据成员仍是隐藏的,达到了共享与安全两全。

然而这些还不是数据共享的全部。对象与对象之间也需要共享数据。

类的静态成员是解决同一个类中不同对象之间数据和函数共享问题的。

例如可以抽象出某公司全体雇员的共性,设计如下雇员类:

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累加,所有对象共同维护同一个count
		count++;
	}
	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累加,所有对象共同维护同一个count
		count++;
	}
	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累加,所有对象共同维护同一个count
		count++;
	}
	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。
在这里插入图片描述
这个程序与第一个例子的输出结果完全相同。相比而言,采用静态函数成员的好处就是可以不依赖任何对象,直接访问静态数据。

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

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

相关文章

解决运行flutter doctor --android-licenses时报错

问题描述&#xff1a; 配置flutter环境时&#xff0c;会使用flutter doctor命令来检查运行flutter的相关依赖是否配好。能看到还差 Android license status unknown.未解决。 C:\Users\ipkiss.wu>flutter doctor Flutter assets will be downloaded from https://storage.…

【MySQL】数据库的增删查改+备份与恢复

文章目录 一、创建数据库create二、数据库所使用的编码2.1 查询字符集和校验集2.2 指定编码创建数据库2.3 不同的校验集对比 三、删除数据库drop四、查看数据库show五、修改数据库alter六、数据库的备份与恢复6.1 备份 mysqldump6.2 恢复source6.3 仅备份几张表或备份多个数据库…

互联网宠物医院开发:宠物健康护航的新选择

随着人们对宠物的关注度逐渐提高&#xff0c;宠物医疗服务也得到了更多的关注和需求。而互联网宠物医院作为一种新兴的医疗服务形式&#xff0c;正以其独特的优势和便捷性受到越来越多宠物主人的青睐。   首先&#xff0c;互联网宠物医院的最大优势在于提供了便捷的服务。宠物…

寻找峰值——力扣162

文章目录 题目描述法一 寻找最大值法二 二分法 题目描述 法一 寻找最大值 int findPeakElement(vector<int>& nums){return max_element(nums.begin(), nums.end()) - nums.begin();}法二 二分法 int findPeakElement(vector<int>& nums) {int l 0, r n…

onnx模型的保存与使用

1 onnx模型的保存 在网络训练结束之后&#xff0c;通常会将模型的权重参数保存到.pth或.pt文件中&#xff0c;如果部署环境中有pytorch&#xff0c;那么直接新建一个模型类对象&#xff0c;然后导入权重参数即可&#xff0c;但如果部署环境中只有OpenCV&#xff0c;没有pytorc…

融合大数据、物联网和人工智能的智慧校园云平台源码 智慧学校源码

电子班牌系统用以展示各个班级的考勤信息、授课信息、精品课程、德育宣传、班级荣誉、校园电视台、考场信息、校园通知、班级风采&#xff0c;是智慧校园和智慧教室的对外呈现窗口&#xff0c;也是学校校园文化宣传和各种信息展示的重要载体。将大数据、物联网和人工智能等新兴…

27岁到来之际,我在阿里实现了年薪30W+的小目标

毕业快 5 年了&#xff0c;每当和人聊起自己的职场飞升之路&#xff0c;都不由得感激当初果断逃离舒适圈的自己。出身一所非 211、985 院校&#xff0c;毕业后入职了一家小型互联网公司&#xff0c;当着普普通通的初级测试工程师&#xff0c;工作期间虽然也时常遇到挑战&#x…

性能优化点

Arts and Sciences - Computer Science | myUSF 索引3层&#xff08;高度为3&#xff09;一般对于数据库地址千万级别的表 大于2000万的数据进行分库分表存储 JVM整体结构及内存模型 JVM调优&#xff1a;主要为减少FULL GC的执行次数或者减少FULL GC执行时间 Spring Boot程序…

在线文档管理工具都有什么值得推荐的?

在线文档管理工具是现代企业和个人必备的工具之一&#xff0c;它们可以帮助用户方便地创建、编辑、共享和管理文档。 几个值得推荐的在线文档管理工具&#xff1a; Google 文档&#xff1a;Google 文档是一款免费的在线文档工具&#xff0c;它提供了和 Microsoft Word 类似的…

微信公众号开发学习

申请测试号 地址 通过F12抓取体验接口权限表的HTML 解析HTML 引入pom <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><…

物联网|可变参数的使用技巧|不一样的点灯实验|访问外设的寄存器|操作寄存器实现点灯|硬件编程的基本流程-学习笔记(11)

文章目录 可变参数的使用技巧第三阶段-初级实验Lesson5:不一样的点灯实验---学习I/O的输出 ☆点灯的电路图分析1 一起看看点灯的电路图Tip1:另一种点灯的电路Tip1:如何访问外设的寄存器2 STM32F407中操作GPIO的方法 通过直接操作寄存器实现点灯实验Tip1:硬件编程的基本流程 2代…

数据可视化(4)散点图及面积图

1.简单散点图 #散点图 #scatter(x,y) x数据&#xff0c;y数据 x[i for i in range(10)] y[random.randint(1,10) for i in range(10)] plt.scatter(x,y) plt.show()2.散点图分析 #分析广告支出与销售收入相关性 dfcarpd.read_excel(广告支出.xlsx) dfdatapd.read_excel(销售…

VS开发Qt程序,无法打印QDebug调试信息,VS进行Qt开发时Qt Designer无法使用“转到槽”选项

VS开发Qt程序&#xff0c;无法打印QDebug调试信息&#xff0c;VS进行Qt开发时Qt Designer无法使用“转到槽”选项 VS开发Qt程序&#xff0c;无法打印QDebug调试信息VS进行Qt开发时Qt Designer无法使用“转到槽”选项 VS开发Qt程序&#xff0c;无法打印QDebug调试信息 解决方案…

使用Idea提交项目到远程仓库

使用Idea提交项目到远程仓库 1.在Idea中打开本地要推送的项目2.创建远程仓库并提交 1.在Idea中打开本地要推送的项目 tips: 首先你得有git工具&#xff0c;没有的话可以参考下面的这篇文章 git与gitee结合使用&#xff0c;提交代码&#xff0c;文件到远程仓库 从导航栏中选择 V…

如何快速开拓海外华人市场?附解决方案!

开拓华人市场对于企业来说是非常必要的。华人市场庞大且潜力巨大&#xff0c;拥有巨额的消费能力。随着华人经济的不断增长&#xff0c;越来越多的企业开始意识到华人市场的重要性。 通过开拓华人市场&#xff0c;企业可以获得更多的销售机会&#xff0c;并且在竞争激烈的市场…

Go语言time库,时间和日期相关的操作方法

time库 用于处理时间、日期和时区的核心库。在实际开发中&#xff0c;常常需要与时间打交道&#xff0c;例如记录日志、处理时间差、计算时间间隔等等。因此&#xff0c;掌握time库的使用方法对于Go开发者来说非常重要。 在Go语言中&#xff0c;时间表示为time.Time类型&…

嵌入式开发的学习内容和技能包括:

. 熟悉C语言编程 掌握基础电子知识&#xff0c;如数字电路、模拟电路和单片机 .熟练掌握嵌入式操作系统的原理、内核架构和应用&#xff0c;如Linux、RTOS等 了解各种外设接口及其驱动程序开发&#xff0c;如SPI、I2C、USART等 熟悉常用的嵌入式开发工具和软件工程流程&#…

【ASPICE】:学习记录

学习记录 ASPICE中文资料什么是ASPICE过程参考模型 ASPICE全称“Automotive Software Process Improvement and Capability dEtermination”&#xff0c;即“汽车软件过程改进及能力评定”模型框架 ASPICE中文资料 主要资料来源 什么是ASPICE 过程参考模型

神经网络原理概述

文章目录 1.神经元和感知器1.1.什么是感知器1.2.什么是单层感知器1.3.多层感知机&#xff08;Multi-Layer Perceptron&#xff0c;MLP&#xff09; 2.激活函数2.1.单位阶跃函数2.2.sigmoid函数2.3.ReLU函数2.4.输出层激活函数 3.损失函数4.梯度下降和学习率5.过拟合和Dropout6.…

python学到什么程度算入门,python从入门到精通好吗

本篇文章给大家谈谈python学到什么程度算入门&#xff0c;以及python从入门到精通好吗&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 学习 Python 之 进阶学习 一切皆对象 1. 变量和函数皆对象2. 模块和类皆对象3. 对象的基本操作 (1). 可以赋值给变量(2). …