1、记录原因
之前一直捋不清普通成员和静态成员之间怎么访问,经常忘记,最近学习设计模式的单例模式时,顺便重温《C++ Primer》static关键字,有了一些新的理解,可能不完全正确,如果有误,望读者提出。
2、《C++ primer》上的介绍
我们通过在成员声明之前加上关键字static使其与类关联在一起。和其它成员一样,静态成员可以是public或者private。静态成员的类型可以是常量、引用、指针、类类型等。
举个例子,定义一个类,用它表示银行账户的标准(注意几个static的使用,什么时候使用静态):
class Bank
{
public:
static Account *GetInstance()
{
if(acc == nullptr)//单线程安全版的单例
{
acc = new Bank();//调用自定义的构造函数
}
return acc;
}
static double GetRate()
{
return interestRate;
}
void clear()
{
//释放工作
}
private:
Bank(){}//构造函数私有化
Bank(const Bank&){}//拷贝构造函数私有化
static double interestRate;//基准汇率,该银行所有账户共有一个汇率
static Bank* acc;//一个实例
};
double Bank::interestRate = 3.8;
Bank Bank::acc = nullptr;
我们这里把银行汇率声明成了static,因为所有账户都使用同一套汇率;节省空间是一方面,再者,如果国家规定或者由于业务需要,汇率要调整,只需调整Bank的interestRate的值即可。
这里的实例acc根据需求,可以设置为共有。如果私有的话,那只能通过调用GetInstance()获取;如果共有的话,可以在需要的地方直接使用Account::acc获取
(单例模式要注意的几点,放在下一节讲)
说明:
(1)类的静态成员存在于类对象之外,所有对象可共用(如果static是私有成员,只能通过类对象访问;如果是共有,可以通过类名加作用域直接访问,比如Bank::acc),对象中不包括任何与静态数据成员有关的数据。
(2)静态成员及函数不与任何对象绑定,它不包含this指针,所以static成员不能直接访问类普通成员(好理解,因为类普通成员都缺省this指针,也就是说类内函数使用普通成员,比如interestRate,完整应该是this->interestRate)
(3)静态成员函数不含this指针,作为结果,静态成员函数不能声明成const,也不能在static函数体内使用this指针。这一限制既适用于this的显式使用,也对调用非静态成员的隐式使用有效。
(4)虽然静态成员不属于某个对象,但我们可以使用类对象、引用或指针来访问静态成员(这里设计成了单例,没法创建对象,否则可以使用: Bank b; b.GetRate();)
(5)因为静态成员不属于某个对象,所以没法用构造函数来初始化静态成员,但是可以在构造函数中改变它的值
(成员初始化提一点,如果类成员是常量或者引用类型,只能通过类的委托构造函数来初始化:
class A
{
A(string str) {//...}
A(int i,string str):A(str){}//委托上一个构造函数来创建
};
)
(6)穿插一个小知识点,有人可能疑惑,成员变量acc可不可以不用指针,而是Bank类型。不行!原因是Bank成员必须都是完全类型。这么说吧,类在创建的时候需要知道自身大小才能创建,类非静态成员是占类空间的,如果Bank中包含自身作为成员,比如它有int类型成员4个字节,指针4个字节,还有自身成员。接下来需要计算它自身占多大空间呢?好,包含一个int类型4个字节,指针4个字节,自身。。。发现没有,陷入死循环,没有办法定自身大小。
3、单例模式要注意的几点
(1)构造函数和拷贝构造函数一定要自己写,而且写成私有成员,目的就是防止外部定义多个对象,因为编译器生成的默认构造函数是共有的。
(2)实例acc为单例,归所有Bank共有,那需要使用static声明
(3)作为单例模式,是不可以创建对象的,故需要把获取单例的接口GetInstance()声明为static,从而可以使用Bank::GetInstance()来获取
(4)作为设计模式原则之一,单一职责(一个类仅有一个引起它变化的方向),单例模式这里设计的时候最好是只有创建工作,不要融合过多的接口
4、什么场景使用静态成员
当该成员被所有类对象共用时,使用static 一是可以节省内存开销,二是当该成员值需要改动时,所有对象都可以自动更改到新值。
5、总结
静态和非静态之间的访问机制,简单总结就是一点:类的非静态成员可以访问自身类的静态成员,而类的静态成员因为不占该类空间(相当于只是提供给该类一个访问权限),故不含this指针,没法访问非静态成员。