在实例之间共享方法的局部静态变量有任何风险吗?

Any risk of sharing local static variable of a method between instances?

假设我创建了:

class Hello {
    public:
        int World(int in)
        {
            static int var = 0;    // <<<< This thing here.
            if (in >= 0) {
                var = in;
            } else {
                cout << var << endl;
            }
        }
};

现在,如果我这样做:

Hello A;
Hello B;

A.World(10);
A.World(-1);
B.World(-1);

我得到的输出是“10”,后面跟着另一个“10”。方法的 local 变量的值刚刚从 class 的一个实例跨越到另一个

这并不奇怪 - 从技术上讲,方法只是带有隐藏 this 参数的函数,因此静态局部变量的行为应该与普通函数一样。但是保证吗?它是标准强制执行的行为,还是仅仅是编译器处理方法的副产品?换句话说 - 这种行为可以安全使用吗? (...超出了让不习惯的人感到困惑的标准风险...)

是的。函数是否是 class 的 [非静态] 成员并不重要,它保证只有一个静态变量实例。

对此类变量的正确技术解释是,这些变量是具有 static durationinternal linkage 的对象 - 因此这些名称在程序退出之前一直有效,并且该名称的所有实例都指向同一个实体。

如果我们谈论的是 Windows 编译器,那是有保证的

https://msdn.microsoft.com/en-us/library/y5f6w579.aspx

The following example shows a local variable declared static in a member function. The static variable is available to the whole program; all instances of the type share the same copy of the static variable.

他们使用的示例与您的非常相似。

我不知道 GCC

是的,有保证。现在,要回答 "Any risk of sharing local static variable of a method between instances?" 这个问题,可能不太直截了当。变量的初始化和使用可能存在潜在风险,这些风险特定于方法的局部变量(而不是 class 个变量)。

对于初始化,标准中的相关部分是 6.7/4 [stmt.dcl]:

Dynamic initialization of a block-scope variable with static storage duration (3.7.1) or thread storage duration (3.7.2) is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.

在简单的情况下,一切都应该按预期工作。当变量的构造和初始化比较复杂时,就会有这种情况特有的风险。例如,如果构造函数抛出异常,它将有机会在下一次调用时再次抛出异常。另一个例子是递归初始化,这是未定义的行为。

另一个可能的风险是方法的性能。编译器需要实现一种机制来确保变量的初始化符合要求。这是依赖于实现的,它很可能是检查变量是否已初始化的锁,并且每次调用该方法时都可以执行该锁。发生这种情况时,会对性能产生重大不利影响。

只有一件事要添加到正确答案中。如果您的 class 是模板化的,那么 var 的实例只会在相同实例化类型的对象之间共享。所以如果你有:

template<typename C>
class Hello {
    public:
        int World(int in)
        {
            static int var = 0;    // <<<< This thing here.
            if (in >= 0) {
                var = in;
            } else {
                cout << var << endl;
            }
        }
};

然后:

Hello<int> A;
Hello<int> B;
Hello<unsigned> C;

A.World(10);
A.World(-1);
B.World(-1);
C.World(-1);

那么最终输出将是“0”而不是“10”,因为 Hello<unsigned> 实例化将有自己的 var.

副本