经验告诉我们,能不使用全局变量就不要使用全局变量,但是全局变量的一些好处又诱使我们去用它。但是是如果不注意,会出现一些很棘手的问题。使用全局变量,一定慎重考虑全局变量的初始化顺序问题。下面几种情况会出现问题,但是比较隐蔽:
下述代码在某些机器上,Y的值可能是不定的。
static const int X = 10; static const int Y = X;
实际项目中的例子
为了管理方便,字体的颜色通常都是集中放到一个类或者命名空间中。如下(假设此文件为defs.h):
namespace VeryLongNameSpace { int COLOR_WHITE[] = {255, 255, 255}; int COLOR_BLACK[] = {0, 0, 0}; }
在x.cpp中使用的时候,由于直接使用VeryLongNameSpace::COLOR_WHITE太长了,所以就重新定义了一下
static int COLOR_WHITE[] = VeryLongNameSpace::COLOR_WHITE;
这样在台式机上运行正常,但是到了手机上运行,显示出来文字的图像就有问题。究其原因,还是全局变量初始化化顺序不定导致的。
要在x.cpp中重新定义,正确的做法有二:一是使用宏,二是使用函数。
// 方式一 #define COLOR_WHITE VeryLongNameSpace::COLOR_WHITE // 方式二 inline int[] COLOR_WHITE() { return VeryLongNameSpace::COLOR_WHITE; }
一般情况下,使用函数可以规避全局变量相互依赖的问题,但是还有一种比较隐蔽的情况,即函数依赖于某个在main中才初始化的对象。
实际项目中的例子
在手机应用开发过程中,为了适应不同屏幕,一种做法是根据屏幕尺寸对坐标进行转换,但是像下面这样的使用方式就会有问题。
首先有定义:
class Device { public: static void Init() { m_mat = new Matrix; // 其它初始化 } static Point tranform(const Point& pt) { return m_mat*pt; } private: Matrix m_mat; }
其次,在main中调用获取到设备信息后,调用
Device::Init()
然后在y.cpp中定义了全局变量
static const Point MY_POINT = Device::Transform(Point(50, 30));
这个MY_POINT的值也是不确定的。严重的情况下,直接出现莫名其妙的崩溃。
这种情况下,用上一种情况所用的两种方法都可以很好地解决。
局部静态变量的使用有一些好处,比如可以保存状态,而且让此状态的封装性比较好。但是由此带来的问题有时也是很严重的,比如类ObjectA中的一个方法中具有局部静态变量,那么不同的对象在调用此方法时是具有依赖的。
class ObjectA { public: int foo() { static int x = -1; int y = x; if(-1 == x) { x = 8; } return y; } }
ObjectA的不同对象在调用此方法的时候是会相互依赖的。另外,类的非静态成员变量也会造成对象间的相互依赖。
对于一些容器,比如vector,在使用之前尽量要将其重置,即clear/reserve等。实际项目中,在一个单体的某方法中使用了vector::push_back,并且这个方法一天只会调用一次,在push_back之前没有做clear的工作。当程序不关闭,第二天再次调用到此方法时,就出现问题了。