持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
1、问题背景
比如说我们有一个平面直角坐标系,然后创建一个Point类,里面有设有(x,y)横纵坐标的成员变量,和一个totalPoint的变量去描述平面直角坐标系上点的个数。这时我们会发现每一个实例化的对象都有(x,y)横纵坐标,所有实例化的对象共用一个totalPoint。这就意味着totalPoint不能像(x,y)横纵坐标一样是普通的成员变量,如果把totalPOint设置成与(x,y)横纵一样的普通成员变量,那么每实例化一个对象都会产生一个totalPoint的副本,就会在成冗余。如果把totalPoint设置为全部变量,则又失去了一定隐蔽性,无法实现数据的隐藏。所以就产生的静态成员来解决这个问题。
2、静态成员变量
#include<iostream>
using namespace std;
class Point
{
public:
//构造函数,每构造一个坐标点就countP加一
Point(int xx,int yy=0)
{
this->X = xx;
this->Y = yy;
totalPoint++;
}
//析构函数,每析构一个坐标点就countP减一
~Point()
{
totalPoint--;
}
//拷贝构造函数
Point(Point &p)
{
this->X = p.X; this->Y = p.Y; totalPoint++;
}
int GetX() { return X; }
int GetY() { return Y; }
int GetCountP() {return totalPoint;}
private:
int X, Y;
static int totalPoint;//点的数量
//此处不可赋值,隐蔽性好,可共享,没有副本不冗余。
};
int Point::totalPoint = 0;//在文件作用域内初始化
int main()
{
Point A(4, 5);
cout << "Point A," << A.GetX() << "," << A.GetY() << endl;
A.GetCountP();
Point B(A);
cout << "Point B," << B.GetX() << "," << B.GetY() << endl;
cout << "totalPoint:" << A.GetCountP() << endl;
cout << "totalPoint:" << B.GetCountP() << endl;
//countP是A、B共享的成员变量。
}
上述案例中Point类中,把totalPoint设置为静态成员变量,创建A点时totalPoint=1,创建B点时totalPoint=2;
3、静态成员函数
上面的代码也有一个小bug,我们获取totalPoint需要通过调用成员函数GetCountP(),而想要调用GetCountP()有需要先实例化一个对象,这样一来totalPoint最小等1,但是平面直角坐标系上可以没有点,totalPoint可以为0。那么怎么修复这个bug呢,答案是静态成员函数,和静态成员变量的思想是一样的,静态成员变量是不属于某一个实例化的对象的,而是属于这个类;同理,静态成员函数也不是属于某一个实例化的对象的,而是属于这个类的。
static int GetCountP() {return totalPoint;}//依法很简单,加个static就🆗了。
4、使用静态成员和静态成员函数的注意事项
1、不能使用类名来调用类的非静态成员函数
void fun()
{
Point ::GetCountP();//正确,GetCountP()是静态成员函数
Point ::GetX();//错误,GetX()是非静态成员函数
}
2、通过类实例化的对象可以调用静态成员函数和非静态成员函数
void fun2()
{
Ponit A(1,2);
A.GetCountP();//正确
A.GetX();//正确
}
3、静态成员函数不能引用非静态成员。因为静态成员函数属于整个类,在类实例化之前就已经分配好空间了,而类的非静态成员必须在类实例化之后才有内存空间。
4、类的非静态成员函数可以调用静态成员函数,但反之不能。
5、类的静态成员函数必须先初始化在使用
5、实验验证
一个类中啥也没有,它所占字节大小为1。
加了一个非静态成员变量(int)以后,所占字节大小为4。
再加一个静态成员变量(int),结果还是4。
做个减法,如果只有静态成员变量 (int),结果应该为1:
猜想正确。