当前课程知识点:C++语言程序设计基础 > 第5章 数据的共享与保护 > 类的静态成员 > 静态数据成员(例5-4)
l静态数据成员
n 用关键字static声明
n 为该类的所有对象共享,静态数据成员具有静态生存期。
n 必须在类外定义和初始化,用(::)来指明所属的类。
l例5-4 具有静态数据成员的Point类
//5_4.cpp
#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 << " Object count = " << count << endl;
}
private: //私有数据成员
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
int main() { //主函数
Point a(4, 5); //定义对象a,其构造函数回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //输出对象个数
Point b(a); //定义对象b,其构造函数回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
b.showCount(); //输出对象个数
return 0;
}
运行结果:
Point A: 4, 5 Object count=1
Point B: 4, 5 Object count=2
大家好
欢迎回来继续学习
C++语言程序设计
这一节
我给大家介绍类的静态数据成员
我们知道类的静态成员
它是用static修饰的
属于整个类
而不属于对象的
那么类的静态数据成员呢
就是为类的所有对象
所共享的数据
它的声明要放在类体里面
但是它的定义和初始化
要放在类外
现在我们来看一个
静态数据成员的例子
这个例子是一个
具有静态数据成员的Point类
我们在前面例子中
看到这个Point类
它的数据成员就是一个x坐标
一个y坐标
但是如果我们想存放点的总数
怎么存放呢
也就是说
我们希望能够记录一下
到某个时刻为止
一共在这个系统中
构造了多少个Point对象
这个数值要记录在哪儿呢
当然由于它是与Point类相关的
肯定我们要想到
它应该放在Point类里面
但是它又不应该
放在每一个对象里面
每一个对象要负责存储
自己的x坐标 y坐标
但是每一个对象不应该去存储
整个类现在
一共有多少个对象这件事
所以呢像这样的数据
就是属于整个类
为整个类的所有对象所共有
那么就应该把它定义成
类的静态成员
类的静态成员在定义的时候
要加一个static关键字来修饰
我们看这个是Point类的类图
在这里面呢
我们看到有一个count成员
那么它是类的静态成员
接下来呢
我们来看一下
这个Point类的代码
现在我们通过这个例子
来观察一下类的静态数据成员
这是一个点类的定义
我们定义好一个点类以后
可以在需要的时候
构造很多点的对象
那么这个时候如果需要
存储点的总数
那么这样一个变量呢
作为类的静态变量就比较合适
所以呢在这个点类中
Point类中
我们定义了一个静态变量count
这个count在类体里面只是声明
count的定义以及初始化
要放在类体之外 在这儿
除了这个静态成员以外
还有非静态的数据成员x和y
这个非静态的成员x y呢
就属于每一个点了
每一个点都要有个x坐标 y坐标
但是每一个点
不会存储这个count值
这个count值只在整个类中
存储一份
然后我们看这个类里面
它在构造函数中
就要给count进行更新
每构造一个点
点的总数就要加1
我们看复制构造函数中也是
复制构造函数也是构造了
也要给点数加1
析构函数中呢
就要给点的总数减1
那么学第四章的时候
是不是有的同学还觉得
这个析构函数有什么用啊
老觉得它没有用
空函数体写它干什么
现在你看到
在这个例题中
析构函数有用了吧
每一个点当它消亡的时候
都会执行析构函数
这个时候就是特别合适的地方
给点数减1的地方
而且从这些例题我们会看到
很多时候
一般来讲很多操作
在构造函数跟析构函数中
都是相对的
比如说构造函数中
给点的总数加1
那么析构函数呢
就要给点的总数减1
以后我们还会看到
更多这样的例子
其他的函数呢
跟我们以前的例子中
看到的都是一样的
只不过这儿呢
多了一个函数 showCount
就是显示一下
目前一共有多少个点了
大家看输出这个count
showCount是一个普通的函数
不是静态函数
通过每一个点都可以调用它
那也就是说
通过每一个点都可以去访问
这个count变量
接下来我们看一下
在主函数中是怎么应用
在主函数中
首先构造了一个点
它初始化坐标是4和5
x是4 y是5
然后接下来我们验证一下
输出一下点a的x坐标 y坐标
大家看这里
输出结果看到了
好 那接下来就通过a
去调用showCount
showCount这个非静态的函数
是要通过对象来调用的
这种消息是要发给对象的
那么通过对象调用showCount
就可以显示目前点的个数是多少
我们看到
这儿点的个数输出是1
下面又通过复制构造函数
去初始化了一个新的点b
让它跟a一样
同样呢构造完了以后
输出它的x值 y值
验证一下看
x坐标和y坐标跟a是一样的
复制构造
效果就应该是这样的
接下来通过b
再次调用showCount
我们可以看到
通过每一个对象
都可以去调用showCount
这个函数
都可以得到当前点的总数
那么通过b去调用showCount呢
看到点的总数是2
为什么呢
因为我们构造a的时候
调用的是构造函数
在这里面给count给增了1
构造点b的时候
调用了复制构造函数
也给count增1
它们操作的
都是同一个静态变量count
所以构造完了b
这个count的值输出就是2了
当我们离开这个主函数的时候
也就是说程序要结束的时候
对象a 对象b
都要被析构的
它们的存储空间都要被释放
所以都要被析构的
这个时候就会调用析构函数
每析构一个变量呢
将点的总数减1
所以通过这个例题我们看到了
可以在类里面
定义一个静态的变量
用来为整个类的所有对象所共享
存这种共享的数据
但是现在我问一个问题
大家看怎么解决
也就是说
在主函数中
当我一个点都没有的时候
我能不能问一问现在有几个点了
0个吗 对吧
a也没有构造 b也没有构造
一个点都没有的时候
是不是我可以查询一下
现在有多少个点
是不是也应该能够告诉我
现在点的个数是0呢
看看现在这个类
有办法做到这一点吗
做不到
为什么
count这个变量是私有成员
所以在类外是不能直接访问的
那好 通过公有接口访问呢
通过showCount访问呢
但你怎么调用showCount呢
只能用
对象名.函数名的方式调用
对吧
你没有对象了
你就没有办法调用这个函数
那是不是这个问题
就没有办法解决了呢
是不是在所有的点构造之前
没有点的时候
我就查不到当前的点数呢
当然也会有别的办法
后面的例题中我们会看到的
这个例题我们就先做到这些
-导学
--第1章导学
-计算机系统简介
--计算机系统简介
--计算机系统简介 测试题
-计算机语言和程序设计方法的发展
--计算机语言和程序设计方法的发展 测试题
-面向对象的基本概念
--面向对象的基本概念 测试题
-程序的开发过程
--程序的开发过程
--程序的开发过程 测试题
-信息的表示和储存
--计算机的数字系统
--数据的编码表示
--信息的表示和储存 测试题
-实验指导
-导学
--第二章导学
-C++语言概述
--C++语言概述 测试题
-基本数据类型、常量、变量
--程序举例
--基本数据类型、常量、变量 测试题
-运算与表达式
--运算与表达式 测试题
-实验二:简单程序设计(上)
-数据的输入和输出
--数据的输入和输出
--数据的输入和输出 测试题
-选择结构
--if语句
--switch语句
--选择结构 测试题
-循环结构
--for语句
--循环结构 测试题
-自定义类型
--自定义类型
--自定义类型
-第2章小结
--第二章小结
-实验二:C++简单程序设计(下)
-导学
--导学
-函数定义
--函数定义
--函数定义 测试题
-函数调用
--例3-2
--例3-3
--例3-4
--例3-5
--例3-6
--函数调用 测试题
-嵌套与递归
--例3-9
--例3-10
--嵌套与递归 测试题
-函数的参数传递
--函数的参数传递
--函数的参数传递 测试题
-引用类型
--引用类型 测试题
-含有可变参数的函数
--含有可变参数的函数 测试题
-内联函数
--内联函数 测试题
-constexpr函数
--CONSTEXPR函数课后习题
-带默认参数值的函数
--带默认参数值的函数 测试题
-函数重载
--函数重载 测试题
-C++系统函数
--C++系统函数习题
-第3章小结
--第三章小结
-实验三(上)函数的应用
-实验三(下)函数的应用
-导学
--导学
-面向对象程序的基本特点
--面向对象程序的基本特点 测试题
-类和对象
--类和对象的定义
--类和对象 测试题
-构造函数
--构造函数基本概念
--委托构造函数
--复制构造函数
--构造函数 测试题
-析构函数
--析构函数
--析构函数 测试题
-类的组合
--类的组合
--类的组合程序举例
--前向引用声明
--类的组合 测试题
-UML简介
--UML简介
--UML简介课后习题
-结构体与联合体
--结构体与联合体 测试题
-枚举类
--枚举类
--枚举类 测试题
-第4章小结
--第四章小结
-实验四(上)
--实验四(上)
-实验四(下)
--实验四(下)
-导学
--导学
-标识符的作用域与可见性
--标识符的作用域与可见性 测试题
-对象的生存期
--对象的生存期
--对象的生存期 测试题
-类的静态成员
--类的静态成员 测试题
-类的友元
--类的友元 测试题
-共享数据的保护
--共享数据的保护 测试题
-多文件结构和预编译命令
--多文件结构和预编译命令 测试题
-第5章小结
--小结
-实验五
--实验五
-导学
--导学
-数组的定义与初始化
--数组的定义与使用
--一维数组应用举例
--数组的定义与初始化 测试题
-数组作为函数的参数
--数组作为函数的参数 测试题
-对象数组
--对象数组
--对象数组 测试题
-基于范围的for循环
-指针的定义和运算
--指针的定义和运算 测试题
-综合实例
--综合实例
-实验六(上)
--实验六上
-指针与数组
--指针数组
--指针与数组 测试题
-指针与函数
--指针类型的函数
--指向函数的指针
--指针与函数 测试题
-对象指针
--对象指针
--对象指针 测试题
-动态内存分配
--动态内存分配 测试题
-智能指针
--智能指针
-vector对象
--vector对象
--vector对象 测试题
-对象复制与移动
--移动构造
--对象复制与移动 测试题
-字符串
--C风格字符串
--string类
--字符串 测试题
-第6章小结
--第六章小结
-综合实例
--综合实例
-实验六(下)
--实验六(下)