对于特定类型的全体对象而言,有时候可能需要访问一个全局的变量。比如说统计某种类型对象已创建的数量。如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量,这时我们可以用类的静态成员来解决这个问题。
静态成员和静态成员函数在使用时可以直接用类名加域运算符使用。也可以用对象.的方法(即使这样也不会传递this指针给非静态成员函数),但不推荐,因为这样容易产生歧义,实际上他们并不相关。
static成员在类体内的仅仅是引用性声明,不允许初始化!必须在类定义体外进行定义性声明与初始化!且不能加static。。。特殊的是static const int类型的成员可以在类体内初始化,也可以不在类体外重新定义。
static成员函数不能访问非静态成员,也不能访问非静态成员函数。但非静态成员函数能访问静态成员、静态成员函数。
#ifndef _COUNTEDOBJECT_H_ #define _COUNTEDOBJECT_H_ class CountedObject { public: CountedObject(); ~CountedObject(); static int count_; }; #endif
#include "CountedObject.h" int CountedObject::count_ = 0; //不能加static CountedObject::CountedObject() { ++count_; } CountedObject::~CountedObject() { --count_; }
#include "CountedObject.h" #include <iostream> using namespace std; int main() { cout << CountedObject::count_ << endl; CountedObject col1; cout << CountedObject::count_ << endl; CountedObject *col2 = new CountedObject; cout << CountedObject::count_ << endl; delete col2; cout << CountedObject::count_ << endl; return 0; }
运行结果:
如果count_是私有的,那么就需要定义一个接口来访问他。
static用法总结
1. 用于函数内部修饰变量,即函数内的静态变量。这种变量的生存期长于该函数,使得函数具有一定的“状态”。局部静态变量只在第一次进入函数时初始化。使用静态变量的函数一般是不可重入的,也不是线程安全的,比如strtok(3)。
2. 用在文件级别(函数体之外),修饰变量或函数,表示该变量或函数只在本文件可见,其他文件看不到也访问不到该变量或函数。专业的说法叫“具有internal linkage”,区别于“extern linkage”(简言之:不暴露给别的translation unit)。
在使用全局变量时应注意在.c文件中进行声明,千万不能在.h文件中声明(当被两个文件包含时,会导致重复声明)。
以上是c语言中的两种用法。C++除了以上两种,还有:
3.用于修饰类的数据成员,即所谓“静态成员”。这种数据成员的生存期大于class的对象(实例/instance)。静态数据成员是每个class有一份,普通数据成员是每个instance 有一份。
4. 用于修饰class的成员函数,即所谓“静态成员函数”。这种成员函数只能访问静态成员和其他静态程员函数,不能访问非静态成员和非静态成员函数。
单例模式:保证一个类只有一个实例,并提供一个全局访问点;禁止拷贝。
#include <iostream> using namespace std; class Singleton { public: static Singleton *Getinstance() { if (instance_ != NULL) instance_ = new Singleton; return instance_; } private: Singleton() { } static Singleton* instance_; }; Singleton* Singleton::instance_; int main() { Singleton* s1 = Singleton::Getinstance(); Singleton* s2 = Singleton::Getinstance(); return 0; }
要保证类不被多次实例化,首先要把构造函数声明为私有的。那么又要保证类有一个实例,所以提供一个全局的接口Getinstance,他在instance_为空的时候调用构造函数,在其为非空时之间返回instance_。这样保证了有且只能有一个实例。上述代码不能保证对象不被拷贝或赋值,因此还需把拷贝构造函数和赋值运算符函数声明为私有的:
private: Singleton(const Singleton& other); Singleton& operator=(const Singleton& );
上述代码还有一个缺点,就是类包含动态成员,在程序结束时不会自动释放内存。可以定义一个嵌套类来释放:
#include <iostream> using namespace std; class Singleton { public: static Singleton *Getinstance() { if (instance_ == NULL) instance_ = new Singleton; return instance_; } ~Singleton() { cout << "~Singleton" << endl; } class Garbo { public: ~Garbo() { if (instance_ != NULL) delete instance_; } }; private: Singleton(const Singleton& other); Singleton& operator=(const Singleton& ); Singleton() { cout << "Singleton" << endl; } static Singleton* instance_; static Garbo garbo_; }; Singleton::Garbo Singleton::garbo_;//注意声明方式 Singleton* Singleton::instance_; int main() { Singleton* s1 = Singleton::Getinstance(); Singleton* s2 = Singleton::Getinstance(); return 0; }
运行结果:
还有一种更简单的方法:
#include <iostream> using namespace std; class Singleton { public: static Singleton &Getinstance() { static Singleton instance_; return instance_; } ~Singleton() { cout << "~Singleton" << endl; } private: Singleton(const Singleton& other); Singleton& operator=(const Singleton& ); Singleton() { cout << "Singleton" << endl; } }; int main() { Singleton& s1 = Singleton::Getinstance();//引用不可省,否则会调用拷贝构造函数 Singleton& s2 = Singleton::Getinstance(); return 0; }
运行结果同上。