目录
特点1.static成员为静态成员,为所有类对象所共享(在某种程度上可以理解为全局的,用类去封装"全局变量"),存放在静态区,则不属于某个具体的对象,不能通过初始化列表来初始化
特点2.静态成员变量必须在类外初始化(const修饰的size_t类型除外),定义时不添加static关键字,类中只是声明
如果用const修饰类型为整数类型或枚举类型的静态成员变量(建议不要单独记这个特例,使用通用的语法)
特点3:类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
特点5:静态成员也是类的成员,受public、protected、private 访问限定符的限制
特点6:静态成员函数没有this指针,只要指定类域且静态成员变量在public里面就可以访问
1.static成员变量
问题:实现一个类,计算程序中创建出了多少个类对象
设计思路
使用一个成员变量count实时记录创建出了多少个类对象
调用构造函数:对象个数+1
调用析构函数:对象个数-1
如果某个函数需要使用自定义类型的拷贝,调用拷贝构造函数,对象个数+1
显示一共创建出了多少个对象:GetCount()成员函数返回count的值
代码示例
版本1
class Myclass
{
public:
Myclass()
{
count++;
}
Myclass(const Myclass&)
{
count++;
}
~Myclass()
{
count--;
}
int GetCount()
{
return count;
}
private:
int count=0;
};
但这样做会有问题: count是普通的成员变量,和其对应的对象是绑定的,则count的结果只能是0或者1,即创建多少个对象就有多少个count,违背了题目的意思,题目的意思是用一个count来统计创建了多少个类对象
不建议使用全局变量,因为任何地方都可以随意改变,而且某些情况下会出错,例如:
Myclass function(const Myclass myobj)
{
static Myclass myobj;
count++;
}
多次调用function函数,myobj是静态对象,只会初始化一次,但是每调用一次function函数,count都会++,导致错误的结果
这里要引入static成员,封装能完美解决问题
版本2
static成员
特点1.static成员为静态成员,为所有类对象所共享(在某种程度上可以理解为全局的,用类去封装"全局变量"),存放在静态区,则不属于某个具体的对象,不能通过初始化列表来初始化
特点2.静态成员变量必须在类外初始化(const修饰的size_t类型除外),定义时不添加static关键字,类中只是声明
像下面这样写就是错误的:
(静态成员变量无初始化列表,不能加缺省值)
(静态成员变量无初始化列表)
正确写法:
在类外定义(也仅仅是定义)可以摆脱私有的限制
注:静态成员变量在定义可以摆脱私有,但访问上不能摆脱私有
如果用const修饰类型为整数类型或枚举类型的静态成员变量(建议不要单独记这个特例,使用通用的语法)
既可以在类中定义:
class Myclass
{
public:
//......
private:
const static int count = 0;
};
也可以在类中声明,在类外定义:
class Myclass
{
public:
//......
private:
const static int count;
};
const int Myclass::count = 0;
如果是其他类型用const修饰会报错:
特点3:类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
正确代码:
class Myclass
{
public:
Myclass()
{
count++;
}
Myclass(const Myclass&)
{
count++;
}
~Myclass()
{
count--;
}
static int GetCount()//静态成员函数
{
return count;
}
private:
static int count;
};
int Myclass::count = 0;
测试以下代码:
int main()
{
Myclass myobj1;
cout << myobj1.GetCount() << endl;//对象.静态成员
Myclass myobj2;
cout << myobj2.GetCount() << endl;
return 0;
}
或者也可以这样写:
int main()
{
Myclass myobj1;
cout << Myclass::GetCount() << endl;//类名::静态成员
Myclass myobj2;
cout << Myclass::GetCount() << endl;
return 0;
}
count在正常变动:
练习:求下面代码的打印结果
Myclass function(const Myclass myobj)
{
cout << Myclass::GetCount() << endl;
return myobj;
}
Myclass global_obj;
int main()
{
cout << Myclass::GetCount() << endl;
{
static Myclass myobj0;
Myclass myobj1;
cout << Myclass::GetCount() << endl;
}
cout << Myclass::GetCount() << endl;
Myclass myobj2;
cout << Myclass::GetCount() << endl;
function(myobj2);
cout << Myclass::GetCount() << endl;
return 0;
}
分析
第一次打印:创建了全局对象global_obj,打印结果为1(说明static对象myobj0不会在全局对象前初始化)
第二次打印:创建了静态对象myobj0和局部对象myobj1(),打印结果为3,
第三次打印:myobj1被销毁,myobj0和global_obj都存储在静态区,不受{ }管控,因此打印结果为2
第四次打印:创建了myobj2对象,打印结果为3
第五次打印:调用function(myobj2),由于function接收的参数为const Myclass myobj,需要调用拷贝构造函数,对象+1,count==4,打印结果为4
由于function返回的是对象,因此会再次调用拷贝构造函数创建临时对象,count会短暂变为5
函数结束时,程序会调用两次析构函数,function产生的临时对象和myobj都被销毁,count变回3
第六次打印:打印结果为3
特点4:静态对象只会定义一次
例如以下代码:
Myclass function()
{
static Myclass myobj;
return myobj;
}
int main()
{
function();
cout << Myclass::GetCount() << endl;
function();
cout << Myclass::GetCount() << endl;
return 0;
}
分析:虽然调用两次function函数,但是myobj只会初始化一次,因此打印结果都是1
特点5:静态成员也是类的成员,受public、protected、private 访问限定符的限制
2.static成员函数
上方代码的Myclass类中有这样一个函数:
static int GetCount()//返回静态类型
{
return count;
}
和普通函数不一样的是:GetCount函数的返回类型是static int,则称GetCount是静态成员函数
特点6:静态成员函数没有this指针,只要指定类域且静态成员变量在public里面就可以访问
一般情况下, 静态成员函数和静态成员变量配套出现,例如GetCount()和count静态成员变量
注意:没有this指针就要注意对象的成员变量是不可访问的!
例如以下错误代码:
class Myclass
{
public:
static int GetCount()
{
static int ret=val1;
return ret;
}
private:
int val1=1;
};
错因:静态成员函数中使用非静态成员变量val1,非静态成员变量需要通过对象实例来访问,但静态成员函数没有this指针,因此无法访问到val1
3.静态成员函数的理解误区
之前在CD19.【C++ Dev】类和对象(10) 日期类对象的成员函数(日期+天数)文章中写符取得月份天数的函数
class Date
{
public:
static int GetMonthDay(int year, int month);
//省略其他成员函数
private:
int _year;
int _month;
int _day;
};
可以改成静态成员函数,如下:
//int前不要加static!
int Date::GetMonthDay(int year,int month)
{
static int day[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//默认2月是28天,之后单独判断
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
return day[month];
}
注意:在写GetMonthDay函数的定义时,返回类型不要写成static int!!!类的声明中加了static只是表明是类的一个静态成员函数,和返回值没有任何关系
可以通过如下两种方式调用该函数:
方式1:类名::
#include "Date.h"
int main()
{
cout << Date::GetMonthDay(2025, 4) << endl;//类名::GetMonthDay(2025, 4)
return 0;
}
运行结果:
方式2:对象.
#include "Date.h"
int main()
{
Date d;
cout << d.GetMonthDay(2025, 4) << endl;//对象.GetMonthDay(2025, 4)
return 0;
}
运行结果: