三:静态(static)几个典型的应用:
1. 代替menu的作用(menu hank)
大家都知道如果程序要在,要在类定义的内部直接初始化变量并使用对于以夜的编译器只能使用menu来达到这种效果,代码像这样:
class print
{
menu{count = 10 };//借助枚举来得到一个初始值
string info[count];
};
用MFC编写过程序代码的都应该知道这个功能,它就是通过menu来接初始化类中的IDD然后进行使用,看一面一段MFC产生的代码
///
class CACETESTDlg : public CDialog
{
public:
//……其它成员函数和构造函数的定义
// 对话框数据
enum { IDD = IDD_ACETEST_DIALOG }; //使用menu直接初始化IDD,IDD_ACETEST_DIALOG是对话框的ID值
//……其他成员函数的定义。
};
//实现文件
//……
CACETESTDlg::CACETESTDlg(CWnd* pParent /*=NULL*/)
: CDialog(CACETESTDlg::IDD, pParent)//注:这里直接使用IDD初始化基类。
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
//……
上面这种直接初始并使用的功能是借助menu来实现的。但这有个不好之处就是使menu在这里丧失了本来的作用(menu本身的功能没有得到体现)但有了static之后可以在一个类中使用static常量来达到同样的效果,因为一个类的静态常量被当作一个编译时的常量,而又因为静态具有文件作用域的内部连接,所以不会产生重复定义的冲突。其代码示例在介绍静态成员变量(对象)时已经给出。
不知道大家有不有注意,menu只能使用整型,但类内部静态常量可以是任何类型,甚至抱用户自定义类型。
2. 单件模式
相大家都知道单件模式吧,如果不熟悉可以参考相产模式的书籍,单件模式保证一个类仅有一个实例存在,具体就是通过静态成员变量和静态成员函数以及类的访问层次相结合而实现的,具体代码如下:
//
class Singleton{
public:
static Singleton* Instance();//注:静态成员函数返回类的实例
virtual ~Singleton(){};
protected:
Singleton(){};
private:
static Singleton* _instance;//注:静态成员变量指向类的实例
};
//实现文件:
Singleton* Singleton::_instance = 0;//静态成员变量的初始化
Singleton* Singleton::Instance() //返回指向类实例的静态指针
{
if(_instance == 0)
_instance = new Singleton;
return _instance;
}
/
静态成员函数加静态成员变量便可实现单件模式,其中静态成员函数控制返回类的实例,因为静态成员函数不能为虚函数,因此子类就不能多态的对它进行重定义,这就保证了只能过此接口对类进行使用。用静态成员变量指向那个唯一被实例化的对象,当然最实始被初始化为0,然后使用惰性初始化时行实例的建立,
如果将这些换成全局静态对象和全局静态函数,相信会有不少问题存在J.
3. 消除全局数据
大家都知道,大家都知道,引入全局数据确实在适当的时候能解决一些棘手的问题,但全局数据是跨文件作用域,使得变量的正确初始在有些情况下非常难控制(后面第4点将会讲到),全局变量一旦建立,要消除它是相当难,随着而来便是”名字空间污染”问题,不仅如此,全局变量将变量(对象)和算法代码绑定在一起,使得复用又相当困难。
对于全局变量所存在的这些问题,可以使用封装加静态访问的方法使问题得到优化
首先将全局变量放入一个类或结构中
然后将它们私有化并添加静态成员函数访问接口
例如有下面的全局变量
bool g_flag;
int g_size;
const char* in_modulename;
对于这些全局变量,运用上面的方法介绍的方法进行改进,可以产生下面的代码:
class Global
{
private:
static bool s_flag_;
static int s_size_;
static const char* s_in_modulename_;
private:
Global();//私有的构造函数,使类不能实例化
//....其它相关的函数
public:
//存入相关值
static void setFlag(bool flag){ s_flag_ = flag; }
static void setSize(int size) { s_size_ = size; }
static void setInmodulename(const char* in_modulename){ s_in_modulename_ = in_modulename; }
//取得相关值
static int getFlag() { return s_flag_; }
static int getSize() { return s_size_; }
static const char* getInmodulename() { return s_in_modulename_; }
};
相信这种重构方法通过引入类的封装和静态成员函数的访问来解决全局的问题应该是一个比较好的方法。
4. 解决初始相互依赖问题
如果你让对象A必须在对象B之前初始化,同时又让A的初始化依赖于B已经被初始化,同是A,B又在不同的编译单元,这是我们就无法控制正确的初始化顺序,解决办法还是有的(注,以下直入引用《think in C++》的一段例子,由于是总结,拷贝一段没问题吧J),
这时我们可用由Jerry Schwarz在创建i o s t r e a m库(因为c i n , c o u t和c e r r的定义是在不同的文件中)时提供的一种技术。这一技术要求在库头文件中加上一个额外的类。这个类负责库中静态对象的动态初始化。下面是一个简单的例子:
//DEPEND.H – Static initialization technique
#ifndef DEPEND_H_
#define DEPEND_H_
#include <iostream.h>
extern int x;//Delarations,not definitions
extern int y;
class initializer{
static int init_count;
public:
initializer(){
cout <<"initializer()"<<endl;
if(init_count++ == 0)
{
cout <<"performing initialization"<<endl;
x = 100;
y = 100;
}
}
~initializer(){
cout<< "~initializer()"<<endl;
if(--init_count == 0){
cout << "performing cleanup" <<endl;
}
}
};
//The following creates one object in each
//file where DEPEND.H is included ,but that
//object is only visible within that file:
static initializer init;
#endif //DEPEND_H_
x、y的声明只是表明这些对象的存在,并没有为它们分配存储空间。然而initializer init 的定义为每个包含此头文件的文件分配那些对象的空间,因为名字是static的(这里控制可见性而不是指定存储类型,因为缺省时是在文件范围内)它只在本编译单元可见,所以连接器不会报告一个多重定义错误。
下面是一个包含x、y和init_count定义的文件:
//:DEPDEFS.CPP--Definition
#include “depend.h”
//Static initialization will force
//all these values to zero:
int x;
int y;
int initializer::init_count;
(当然,一个文件的init静态实例也放在这个文件中)假设库的使用者产生了两个其他的文件:
//:DEPEND.CPP—Static initialization
#include “depend.h”
和
//:DEPEND2.CPP –Static initialization
#include “depend.h”
int main()
{
cout << “inside main()” <<endl;
cout << “leaving main()” <<endl;
return 0;
}
现在哪个编译单元先初始化都没有关系。当第一次包含DEPEND.H的编译单元被初始化时,init_count为零,这时初始化就已经完成了(这是由于内部类型的全局变量在动态初始化之前都被设置为零)。对其余的编译单元,初始化会跳过去。清除按相反的顺序,且~initializer()可确保它只发生一次。这个例子用内部类型作为全局静态对象,这种方法也可以用于类,但其对象必须用initializer动态初始化。一种方法就是创建一个没有构造函数和析构函数的类,但用不同的名字的成员函数来初始化和清除这个类。当然更常用的做法是在initializer()函数中,设定指向对象的指针,并在堆中动态创建它们。
注:由于是总结,以后自己查阅方便,最后一点是直接取自《think in C++》是的一段,我觉得这个例子举的非常好!所以已直接拿过来了 :)