09 March 2022

C++ 核心指南目录

I.2: Avoid non-const global variables

理由

没有 const 修饰的全局变量,即可变全局变量,会隐藏依赖关系,从而导致不可预料的结果。比如:

struct Data {
    // ... lots of stuff ...
} data;             // non-const data

void compute()      // don't
{
    // ... use data ...
}

void output()       // don't
{
    // ... use data ...
}

可能还有其他地方会修改数据。这样的话, compute 计算出来的数据结果,在 output 函数中可能不是那个结果,因为中间其他某个地方也会修改这个数据。

警告

全局的对象初始化的时候,不一定完全按顺序进行。因此,如果一定要用全局变量,最好用常数设置全局对象的值。需要注意的是 const 对象的初始化顺序也是无序进行的。

例外

全局对象一般来说比单实例 singleton 好。

注意

全局常量自有其妙用。

此规则也适用于名字空间范围的变量。

其他选项

如果你要用全局(名字空间)变量来避免重复的复制传递参数,可以考虑以指向 const 的引用形式传递数据。另一个解决办法是把数据定义为某个对象的状态,把该数据的操作函数设计成对象的成员方法。

警告

小心避免数据竞争。如果一个线程能访问非局部数据(或作为引用传进来的数据),而另一个线程执行的时候修改该数据,就会造成数据竞争。任何可变数据的指针或引用都可能导致数据竞争。

使用全局指针或引用访问/修改非 const ,非全局的变量,依然避免不了隐藏的数据依赖关系,也可能导致数据竞争情况。

不可变数据不会造成竞争情况。

注意

当然也有例外情况,比如cin, cout,以及 cerr.

强化

清点所有在名字空间作用域的非 const 变量,以及全局范围的指向非 const 数据的指针和引用。